/**
 * @license
 * Copyright 2021 Du Tian Wei
 * SPDX-License-Identifier: Apache-2.0
 */
import * as util from './util.js'
export class NativeUtil {

    static fieldSetter(target, fieldName, register) {
        return (builder, args) => {
            let getter = builder[register][args[1] & 0xfff];
            builder.PushAction((st, f, local, pos) => {
                let v = getter(st, f, local);
                target[fieldName] = v;
                return 1 + pos;
            });
        };
    }
    static fieldGetter(target, fieldName, register) {
        return (builder, args) => {
            builder[register][args[1] & 0xfff] = (st, f, local) => {
                return target[fieldName];
            };
        };
    }
    static objFieldGetter(fieldName, fieldRegister) {
        return (builder, args) => {
            let obj = builder.NObjectRegister[args[1] & 0xfff];
            builder[fieldRegister][args[1] & 0xfff] = (st, f, local) => {
                return obj(st, f, local)[fieldName];
            };
        };
    }
    /**
     *
     * @param {Function} func
     * @param {Number[]} argtype
     * @returns
     */
    static closureVoid(func, argtype) {
        /**
         *
         * @param {OBFunctionBuilder} builder
         * @param {Number[]} args
         * @returns
         */
        let f = (builder, args) => {
            args = args.slice(1);
            let argGetters = argtype.map((rtype, idx) => {
                let idx1 = args[idx] & 0xFFF;
                let v = builder[rtype][idx1];
                if (typeof (v) != 'function') {
                    debugger
                }
                return (st, f, local) => {
                    return v(st, f, local);
                };
            });
            builder.PushAction((st, f, local, pos) => {
                let argVals = argGetters.map(g => g(st, f, local));
                func.apply(null, argVals);
                return pos + 1;
            });
        };
        return f;
    }
    /**
     *
     * @param {Function} func
     * @param {Number[]} argtype
     * @returns
     */
    static closureReturnValue(func, retRegisterType, argtype) {
        /**
         *
         * @param {OBFunctionBuilder} builder
         * @param {Number[]} args
         * @returns
         */
        let f = (builder, args) => {
            let retRegIdx = args[0];
            let retType = (retRegIdx & 0xF000) >> 12;
            retRegIdx = retRegIdx & 0xFFF;
            args = args.slice(1);
            let argGetters = argtype.map((rtype, idx) => {
                let idx1 = args[idx] & 0xFFF;
                let v = builder[rtype][idx1];
                return (st, f, local) => {
                    return v(st, f, local);
                };
            });
            builder[retRegisterType][retRegIdx] = ((st, f, local) => {
                let argVals = argGetters.map(g => g(st, f, local));
                return func.apply(null, argVals);
            });
        };
        return f;
    }
}
export class OBScript {
    NativeLibHash = {}; // libname->hash
    InstalledLibs = {};
    NativeUtil = NativeUtil;
    /**
     * @1type {StructData}
     */
    StructData = {}; //typename->OBStructValueData
    /**
     * @type {Object.<string,OBStructDef>}
     */
    StructDef = {}; // typename-> def
    loadedFunctions; //= {};//function sign->function
    FullNameFSMData = {}; //FullName->OBFSM

    /**
     * @callback FuncInstaller
     * @param {OBFunctionBuilder} funcBuilder
     * @param {number[]}  registersConfig
     */
    /**
     * 安装本地库
     * @param {string} libName
     * @param {string} jsmd5 md5 of js generated config
     * @param {FuncInstaller[]} funcInstallers array of funcInstaller
     */
    InstallLib(libName, jsmd5, funcInstallers) {
        if (this.InstalledLibs[libName]) {
            throw Error("重复导入 " + libName);
        }
        this.InstalledLibs[libName] = funcInstallers;
        this.NativeLibHash[libName] = jsmd5;
    }
    /**
         *
         * @param {string} libname
         * @param {number} funcIdx
         */
    getNativeFunc(libname, funcIdx) {
        if (funcIdx < 0) {
            throw Error("funcIdx:" + funcIdx);
        }
        // Action < UFunctionBuilder, int[] > [] lib;
        let lib = this.InstalledLibs[libname];
        if (lib) {
            if (funcIdx < lib.length) {
                return lib[funcIdx];
            } else {
                throw Error("funcIdx:" + funcIdx + " of lib " + libname + " out of range " + lib.length);
            }
        } else {
            throw Error("Native lib " + libname + " not found");
        }
    }
}
export class OBStructDef {
    Name; //string
    StructCnt; // int
    StringCnt; // int
    IntegerCnt; // int
    FloatCnt; // int
    NobjectCnt; // int
    StructFields; // int
}
export class OBStructValueData {
    /**
     * @type {OBArrayBufferReader}
     */
    Data; //arraybuffer
    FullName;
    Offset;
    Length;
    StructCount;
}
export class OBVariableInfo {
    typeIdx;
    count;

    constructor(typeIdx, count) {
        this.typeIdx = typeIdx;
        this.count = count;
    }
}
export class OBState {
    /**
     *  @type {OBVariableInfo[]}
     */
    Variables;
    /**
     *  @type {string}
     */
    Name;
    MessageHandlers;
    EventHandlers;
}
export class OBCodeSegment {
    name;
    functions;
    fsms;
}
export class OBFunction {
    /**
     *  @type {OBVariableInfo[]}
     */
    Variables;
    instructions;
    /**
     * @type {String}
     */
    Signure;
    Statements;
}
export class OBFSM {
    /**
     * @type {string}
     */
    Name;
    /**
     * @type {Object.<string,OBState>}
     */
    States; //string->state
    /**
     * @type {OBState}
     */
    Entry; //state
    /**
     * @type {OBVariableInfo[]}
     */
    Variables;
    /**
     * @type {string}
     */
    FullName;
    /**
     * @type {string}
     */
    ModuleName;
}
export class OBMessageHandler {
    Name;
    Func;
    ArgTypeName;
}
export class OBEventHandler {
    Name;
    Func;
}
export class OBInstruction {
    Position;
    /**
     *
     * @param {number} code
     * @param {OBFunctionBuilder} builder
     * @param {OBInstruction[]} instructions
     * @param {number} i
     */
    init(code, builder, instructions, i) {

    }
    /**
         *
         * @param {OBFunctionBuilder} funcbuilder
         * @param {OBInstruction[]} instructions
         * @param {number} i
         */
    link(funcbuilder, instructions, i) {

    }
}
export class OBByteCodes {
    static createInstruction(cmd) {
        switch (cmd) {
            //case 0:
            //    break;
            case 1:
                return new PRT();
            case 2:
                return new ARITHI();
            case 3:
                return new ARITHF();
            case 4:
                return new LDSTR();
            case 5:
                return new LDI();
            case 6:
                return new LDF();
            case 7:
                return new RET();
            case 8:
                return new STMT_start();
            case 9:
                return new B_STMT_end();
            case 10:
                return new CHSTT();
            case 11:
                return new STVG();
            case 12:
                return new FSMVS();
            case 13:
                return new FSMVG();
            case 14:
                return new STVS();
            case 15:
                return new MethodCall();
            case 16:
                return new MethodCallRegisterInfoAnchor();
            case 17:
                return new CreateFSM();
            case 18:
                return new FSMSendMsg();
            case 19:
                return new ReceivedMessage();
            case 20:
                return new GetStructField();
            case 21:
                return new SetStructField();
            case 22:
                return new GZ0();
            case 23:
                return new BRIF();
            case 24:
                return new DEC();
            case 25:
                return new BR();
            case 26:
                return new Reg2Var();
            case 27:
                return new Var2Reg();
            case 28:
                return new NOP();
            case 29:
                return new BRIFN();
            case 30:
                return new I2F();
            case 31:
                return new StructFieldDesc();
            case 32:
                return new EQ();
            case 33:
                return new NEQ();
            case 34:
                return new LT();
            case 35:
                return new LTE();
            case 36:
                return new GT();
            case 37:
                return new GTE();
            case 38:
                return new SLF();
            case 39:
                return new NativeMethodCall();
            case 40:
                return new DestroyFSM();
            case 41:
                return new FSMBroadcastMsg();
            case 42:
                return new SGLF();
            case 43:
                return new RAND();
            case 44:
                return new F2I();
            case 45:
                return new FSMSendMsgWait_Data();
            case 46:
                return new FSMSendMsgWait();
            case 47:
                return new FSMBroadcastMsgWait();
            case 48:
                return new TextJoin();
            case 49:
                return new ToString();
            case 50:
                return new Sender();
            case 51:
                return new VOM();
            case 52:
                return new SHL();
            case 53:
                return new AND();
            case 54:
                return new FIX();
            case 55:
                return new LAND();
            case 56:
                return new LOR();
            case 57:
                return new LNOT();
            case 58:
                return new COND();
            case 59:
                return new NEW();
            default:
                throw Error("Unknown byte code command:" + cmd);
        }
        // return new OBInstruction(cmd);
    }
}
export class PositionUpdatePair {
    targetOffset;
    callback;
}
export class OBFunctionBuilder {
    loader; //OBScriptLoader
    StatementLength; //integer
    BuildingFunc; //OBFunction
    PositionUpdatePairList;
    currentInstructPosition; //integer
    CurrentStatementStack = []; //[StatementContext]

    /**
     *  @callback LongRegister
     * @param {OBVMState}
     * @param {OBFunctionBuilder}
     * @param {OBInstruction[]}
     * @returns {number}
     */
    /**
     * @type LongRegister[]
     */
    LongRegister;
    /**
     *  @callback DoubleRegister
     * @param {OBVMState}
     * @param {OBFunctionBuilder}
     * @param {OBInstruction[]}
     * @returns {number}
     */
    /**
     * @type DoubleRegister[]
     */
    DoubleRegister;
    /**
     *  @callback StringRegister
     * @param {OBVMState}
     * @param {OBFunctionBuilder}
     * @param {OBInstruction[]}
     * @returns {string}
     */
    /**
     * @type StringRegister[]
     */
    StringRegister;
    /*@
     *  @callback StringRegister
     * @param {OBVMState}
     * @param {OBFunctionBuilder}
     * @param {OBInstruction[]}
     * @returns {OBStructValue}
     */
    /**
     * @type  {Array} StructRegister[]
     */
    StructRegister;
    /*@
     *  @callback StringRegister
     * @param {OBVMState}
     * @param {OBFunctionBuilder}
     * @param {OBInstruction[]}
     * @returns {object}
     */
    /**
     * @type {Array} NObjectRegister[]
     */
    NObjectRegister;

    constructor(loader) {
        this.loader = loader;
    }

    loadFunctionHeader(reader) {
        let data = this.loader.data;
        let BuildingFunc = new OBFunction();
        this.BuildingFunc = BuildingFunc;

        let header = reader.ReadUInt32();
        let pos = reader.pos;
        this.StatementLength = header * 4 - pos;
        reader.pos = header * 4;
        let nameIdx = reader.ReadUInt32();
        BuildingFunc.Signure = data.GetString(nameIdx);
        this.LongRegister = [];
        this.LongRegister.length = reader.ReadUInt32();
        this.DoubleRegister = [];
        this.DoubleRegister.length = reader.ReadUInt32();
        this.StringRegister = [];
        this.StringRegister.length = reader.ReadUInt32();
        this.StructRegister = [];
        this.StructRegister.length = reader.ReadUInt32();
        this.NObjectRegister = [];
        this.NObjectRegister.length = reader.ReadUInt32();
        let varInfo = [];
        for (let i = 0; i < 5; i++) {
            let info = new OBVariableInfo();
            info.typeIdx = i;
            info.count = reader.ReadUInt32();
            varInfo[i] = info;
        }
        BuildingFunc.Variables = varInfo;
        reader.pos = pos;
    }

    loadStatement(reader) {
        let length = this.StatementLength / 4;
        this.BuildingFunc.instructions = [];
        this.PositionUpdatePairList = []; //[PositionUpdatePair]
        for (let i = 0; i < length; i++) {
            let instPos = reader.pos;
            let code = reader.ReadUInt32();
            let cmd = (code >> 24);
            let inst = OBByteCodes.createInstruction(cmd);
            inst.Position = instPos;
            inst.init(code, this, this.BuildingFunc.instructions, i);
            this.BuildingFunc.instructions[i] = inst;
        }
    }

    link() {
        let instructions = this.BuildingFunc.instructions;
        for (let i = 0; i < instructions.length; i++) {
            let inst = instructions[i];
            this.currentInstructPosition = inst.Position;
            inst.link(this, instructions, i);
        }
        this.PositionUpdatePairList = null;
        this.BuildingFunc.Statements = this.RootStatementContext;
    }

    build() {
        return this.BuildingFunc;
    }

    PositionUpdate(targetOffset, callback) {
        if (this.PositionUpdatePairList == null) {
            throw Error("异常状态");
        }
        let p = new PositionUpdatePair();
        p.targetOffset = targetOffset;
        p.callback = callback;
        this.PositionUpdatePairList.push(p);
    }

    PushAction(Instruction) {
        let stmt = this.CurrentStatementStack[this.CurrentStatementStack.length - 1];
        let newPos = stmt.Actions.length;
        stmt.PushAction(Instruction);
        this.PositionUpdatePairList.forEach((p) => {
            if (p.targetOffset === this.currentInstructPosition) {
                p.callback(newPos);
            }
        });
    }
}

export class OBBuildInFunctions {
    /**
     *
     * @param {OBScript} script
     */
    static install(script) {
        script.InstallLib("", "", [
            OBBuildInFunctions.FSM_FindFsmByTypeInstaller,
            OBBuildInFunctions.FSM_FindFsmByNameInstaller,
            OBBuildInFunctions.Structs_LoadStructFromDatasetInstaller,
            // OBBuildInFunctions.FSM_TargetInstaller,
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Length, 'LongRegister', ['StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_IsEmpty, 'LongRegister', ['StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_IndexOf, 'LongRegister', ['StringRegister', 'StringRegister', 'LongRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_CharAt, 'StringRegister', ['StringRegister', 'LongRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_GetSubstring, 'StringRegister', ['StringRegister', 'LongRegister', 'LongRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToUpperCase, 'StringRegister', ['StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToLowerCase, 'StringRegister', ['StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToTitleCase, 'StringRegister', ['StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Count, 'LongRegister', ['StringRegister', 'StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Replace, 'StringRegister', ['StringRegister', 'StringRegister', 'StringRegister']),
            script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Reverse, 'StringRegister', ['StringRegister']),
        ]);
    }
    /**
     *
     * @param {String} str
     * @returns
     */
    static Text_Reverse(str) {
        if (str) {
            return str.split('').reverse().join('');
        } else {
            return "";
        }
    }
    static Text_Replace(haystack, needle, replacement) {
        needle = needle.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, "\\$1")
            .replace(/\x08/g, "\\x08");
        return haystack.replace(new RegExp(needle, 'g'), replacement);
    }
    static Text_Count(haystack, needle) {
        if (needle.length === 0) {
            return haystack.length + 1;
        } else {
            return haystack.split(needle).length - 1;
        }
    }
    /**
     *
     * @param {String} str
     * @returns
     */
    static Text_ToTitleCase(str) {
        return str.replace(
            /\w\S*/g,
            function (txt) {
                return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
            }
        );
    }
    /**
     *
     * @param {String} str
     * @returns
     */
    static Text_ToLowerCase(str) {
        if (str) {
            return str.toLowerCase();
        }
        return "";
    }
    /**
     *
     * @param {String} str
     * @returns
     */
    static Text_ToUpperCase(str) {
        if (str) {
            return str.toUpperCase();
        }
        return "";
    }
    static Text_GetSubstring(str, from, to) {
        let start = from;
        if (start < 0) {
            start = str.length + start;
        } else {
            start -= 1;
        }
        let end = to;
        if (end < 0) {
            end = str.length + to;
        } else {
            end -= 1;
        }
        if (start > end) {
            let t = start;
            start = end;
            end = t;
        }
        return str.substring(start, end + 1);
    }
    static Text_CharAt(str, index) {
        if (index === 0) {
            return "";
        }
        if (index > 0) {
            return str[index - 1] || "";
        }
        if (index < 0) {
            return str[str.length + index];
        }
    }
    static Text_IndexOf(str, sub, forward) {
        if (forward === 1) {
            return str.indexOf(sub) + 1;
        } else {
            return str.lastIndexOf(sub) + 1;
        }
    }
    static Text_IsEmpty(str) {
        return str.length === 0 ? 1 : 0;
    }
    static Text_Length(str) {
        return str.length;
    }
    static FSM_FindFsmByTypeInstaller(builder, args) {
        let returnRegisterIdx = args[0] & 0xFFF;
        let argIdx = args[1] & 0xFFF;
        let a1 = builder.StringRegister[argIdx];
        builder.NObjectRegister[returnRegisterIdx] = (state, func, locals) => {
            let r = state.fsm.VM.FindRunningFSMByType(a1(state, func, locals));
            return r;
        };
    }
    static FSM_FindFsmByNameInstaller() {
    }
    static Structs_LoadStructFromDatasetInstaller(builder, args) {
        let returnRegisterIdx = args[0] & 0xFFF;
        let idIdx = args[2] & 0xFFF;
        let a1 = builder.LongRegister[idIdx];
        let typeIdx = args[1] & 0xFFF;
        let a2 = builder.StringRegister[typeIdx];
        let StructData = builder.loader.script.StructData;
        builder.StructRegister[returnRegisterIdx] = (state, func, locals) => {
            let r = StructData.Get(a2(state, func, locals), a1(state, func, locals));
            return r;
        };
    }
    static FSM_TargetInstaller(builder, args) {
        let returnRegisterIdx = args[0] & 0xFFF;
        builder.NObjectRegister[returnRegisterIdx] = (state, func, locals) => {
            return state.fsm.Target;
        };
    }
}
export class OBStructValue {
    /**
     * @type {OBStructDef}
     */
    Def;
    /**
     * @type {OBTypedVariableGroup}
     */
    registers;

    constructor(Def) {
        this.Def = Def;
        let registers = new OBTypedVariableGroup(null);
        registers.LongRegister = [];
        registers.LongRegister.fill(0, 0, Def.IntegerCnt);
        registers.DoubleRegister = [];
        registers.DoubleRegister.fill(0, 0, Def.FloatCnt);
        registers.StringRegister = [];
        registers.StringRegister.fill('', 0, Def.StringCnt);
        registers.StructRegister = [];
        registers.StructRegister.length = Def.StructCnt;
        registers.NObjectRegister = [];
        registers.NObjectRegister.length = Def.NobjectCnt;
        this.registers = registers;
    }

    toString() {
        return "Struct." + this.Def.Name;
    }
}
export class StructData {
    /**
     * @type {Object.<string,OBStructDef>}
     */
    StructDef;
    /**
     * @type {Object.<string,OBStructValueData>}
     */
    Groups;
    /**
     * @type {OBArrayBufferReader}
     */
    DataSegment;

    constructor(structDataGroups, data) {
        this.Groups = structDataGroups;
        this.DataSegment = data;
    }
    /**
         *
         * @param {string} type fullname of type
         * @1param {integer} id id of data
         * @param {?Object.<string,OBStructValue>}
         * @returns {OBStructValue}
         */
    Get(type, id, loading) {
        if (type.startsWith("S") && type.endsWith(";")) {
            type = type.substr(1, type.length - 2);
        }
        if (loading == null) {
            loading = {};
        } else {
            let loaded = loading[id + "@" + type];
            if (loaded) {
                return loaded;
            }
        }
        let def = this.StructDef[type];
        let group = this.Groups[type];
        let reader = group.Data;
        let itemStart = 0;
        for (let i = 0; i < group.StructCount; i++) {
            reader.pos = itemStart;
            let length = reader.ReadInt32();
            let itemid = reader.ReadUInt32();
            if (itemid === id) {
                reader.pos -= 4;
                let s = new OBStructValue(def);
                loading[id + "@" + type] = s;
                for (let j = 0; j < def.IntegerCnt; j++) {
                    s.registers.LongRegister[j] = reader.ReadInt32(); //VariableValueSet(j, reader.ReadInt32());
                }
                for (let j = 0; j < def.StringCnt; j++) {
                    let idx = reader.ReadUInt32();
                    let str = this.DataSegment.GetString(idx);
                    s.registers.StringRegister[j] = str; //VariableValueSet(j, str);
                }
                for (let j = 0; j < def.FloatCnt; j++) {
                    s.registers.DoubleRegister[j] = reader.ReadSingle(); //VariableValueSet(j, reader.ReadSingle());
                }
                for (let j = 0; j < def.StructCnt; j++) {
                    let fieldDef = def.StructFields[j];
                    if (fieldDef.startsWith("S")) {
                        let subId = reader.ReadUInt32();
                        let subStruct = this.Get(def.StructFields[j], subId, loading);
                        s.registers.StructRegister[j] = subStruct; //VariableValueSet(j, subStruct);
                    } else if (fieldDef.startsWith("I")) {
                        // TODO
                    } else if (fieldDef.startsWith("N")) {
                        let elementTypeName = fieldDef.substr(1);
                        let structCnt = reader.ReadUInt32();
                        let map = {};
                        for (let k = 0; k < structCnt; k++) {
                            let keyIdx = reader.ReadUInt32();
                            let keyStr = this.DataSegment.GetString(keyIdx);
                            let structId = reader.ReadInt32();
                            let st = this.Get(elementTypeName, structId, loading);
                            map[keyStr] = st;
                        }
                        s.registers.StructRegister[j] = map;
                    }
                }
                return s;
            } else {
                itemStart += length * 4 + 4;
            }
        }
        throw Error("找不到 ID为" + id + "的" + type);
    }
}

export class OBStructDataReader {
    /**
     *
     * @param {OBArrayBufferReader} reader
     * @returns {StructData}
     */
    readStream(reader) {
        let dataLength = reader.ReadUInt32();
        let data = reader.readSub(dataLength);
        return this.readStructData(reader, data);
    }
    /**
     *
     * @param {OBArrayBufferReader} reader
     * @param {OBArrayBufferReader} data
     * @returns {StructData}
     */
    readStructData(reader, data) {
        let length = reader.ReadInt32();
        let structs = {};

        let groupCnt = reader.ReadUInt32();
        for (let i = 0; i < groupCnt; i++) {
            let offset = reader.pos;
            let strIdx = reader.ReadUInt32();
            let FullName = data.GetString(strIdx);
            let structCnt = reader.ReadInt32();
            let length = reader.ReadInt32();
            // arraybuffer
            let bin = reader.readSub(length * 4);
            let info = new OBStructValueData(); //
            info.Data = bin;
            info.FullName = FullName;
            info.Offset = offset;
            info.Length = length;
            info.StructCount = structCnt;
            structs[FullName] = info;
        }
        return new StructData(structs, data);
    }
}
class Relocation {
    /**
     * @1type {Object.<String>:{idx:Number,inited:bool}>}
     */
    string = {};
    /**
     * @1type {Object.<String:{idx:Number,inited:bool}>}
     */
    integer = {};
    /**
     * @1type {Object.<String:{idx:Number,inited:bool}>}
     */
    float = {};
    /**
     * @1type {Object.<String:{idx:Number,inited:bool}>}
     */
    bin = {};
    /**
     * @1type {Object.<String:{idx:Number,inited:bool}>}
     */
    structFieldIndex = {};

    addRelocationString(str) {
        if ((typeof str) !== 'string') {
            throw Error('不是字符串');
        }
        if (!this.string.hasOwnProperty(str)) {
            this.string[str] = {
                idx: 0,
                inited: false
            }
        }
    }
}
export class OBScriptLoader {
    /**
     * @type {OBArrayBufferReader}
     */
    reader; //OBArrayBufferReader
    /**
     * @type {OBArrayBufferReader}
     */
    data; //OBArrayBufferReader
    loadingFunctions = {}; //[OBFunctionBuilder]
    Linkings = []; //Linkable

    /**
     * @callback NativeLibInstaller
     * @param {OBScript} script
     */
    /**
     *
     * @param {ArrayBuffer} arraybuffer of byte code
     * @param {NativeLibInstaller} nativeLibs
     * @returns
     */
    static loadScript(arraybuffer, nativeLibs) {
        let script = new OBScript();
        OBBuildInFunctions.install(script);
        let l = new OBScriptLoader();
        // let nativeLibs = OBNative.functions;
        if (nativeLibs) {
            if (Array.isArray(nativeLibs)) {
                nativeLibs.forEach(installer => {
                    installer(script);
                });
            } else {
                nativeLibs(script);
            }
        }
        l.load(script, arraybuffer);
        return script;
    }
    load(script, buf) {
        this.script = script;
        this.reader = new OBArrayBufferReader(buf);
        this.readXE();
    }

    readXE() {
        let MAG = this.reader.ReadInt32(); //'\u007fUEX';
        if (MAG != 0x5845557F) {
            throw Error("Unknown MAG:" + MAG);
        }
        let version = this.reader.ReadInt32();
        if (version != 1) {
            throw Error("Unsupported version." + version);
        }

        let SegmentCnt = this.reader.ReadInt32();
        let headerEnd = this.reader.pos + SegmentCnt * 2 * 4;

        let codes = [];

        for (let i = 0; i < SegmentCnt; i++) {
            let type = this.reader.ReadInt32();
            let startIn4Bytes = this.reader.ReadUInt32();
            let start = headerEnd + startIn4Bytes * 4;
            let pos = this.reader.pos;
            switch (type) {
                case 0:
                    this.reader.seek(start);
                    this.data = this.loadDataSegment();
                    break;
                case 1:
                    this.reader.seek(start);
                    let code = this.loadCodeSegment(); // data应该是第一个段，所以此时data已经存在
                    codes.push(code);
                    break;
                case 2:
                    this.reader.seek(start);
                    this.script.StructData = this.loadStructDataSegment();
                    this.script.StructData.StructDef = this.script.StructDef;
                    break;
                case 3:
                    this.reader.seek(start);
                    this.script.StructDef = this.loadStructDefDataSegment();
                    break;
                case 4:
                    this.reader.seek(start);
                    this.loadPackageInfo();
                    break;
                default:
                    throw Error("Unknown Segment type:" + type);
            }
            this.reader.seek(pos);
        }
        this.script.loadedFunctions = this.loadingFunctions;
        this.Linkings.forEach(l => {
            l.link();
        });
        codes.forEach(codeSeg => {
            codeSeg.fsms.forEach(fsm => {
                fsm.FullName = codeSeg.name + "." + fsm.Name;
                this.script.FullNameFSMData[fsm.FullName] = fsm;
            });
        });
    }

    loadCodeSegment() {
        let reader = this.reader;
        let data = this.data;

        let start = reader.pos;
        let SegmentReader = reader.getSub(start);

        let header = SegmentReader.ReadUInt32() * 4;
        SegmentReader.pos = header;
        let segment = new OBCodeSegment();
        let nameStringIdx = SegmentReader.ReadInt32();
        let name = data.GetString(nameStringIdx);
        let ufunctions = this.readFunctions(SegmentReader);
        let fsms = this.readFSMs(SegmentReader, name);
        // 字段赋值
        segment.name = name;
        segment.functions = ufunctions;
        segment.fsms = fsms;
        return segment;
    }

    readFSMs(reader, moduleName) {
        let cnt = reader.ReadInt32();
        let f = []; // [OBFSM]
        for (let i = 0; i < cnt; i++) {
            let s = reader.ReadUInt32() * 4;
            let pos = reader.pos;
            reader.pos = s;
            let fsm = this.readFSM(reader);
            fsm.ModuleName = moduleName;
            fsm.FullName = moduleName + "." + fsm.Name;
            reader.pos = pos;
            f[i] = fsm;
        }
        return f;
    }

    readFSM(reader) {
        let data = this.data;
        let nameIdx = reader.ReadUInt32();
        let name = data.GetString(nameIdx);
        let variables = this.readVariables(reader);
        let fucCnt = reader.ReadUInt32();
        // TODO
        let states = this.readStates(reader);
        let entryStateNameIdx = reader.ReadUInt32();
        let entryStateName = data.GetString(entryStateNameIdx);
        let entryState = null;

        // Dictionary<string, UState> stateDict = new Dictionary<string, UState>();
        let stateDict = {};
        for (let i = 0; i < states.length; i++) {
            let s = states[i];
            stateDict[s.Name] = s;
            if (entryStateName === s.Name) {
                entryState = s;
            }
        }
        if (entryState == null) {
            throw Error("Can't find state named " + entryStateName + " FSM " + name);
        }
        let fsm = new OBFSM();
        fsm.Name = name;
        fsm.States = stateDict;
        fsm.Entry = entryState;
        fsm.Variables = variables;
        return fsm;
    }

    readStates(reader) {
        let cnt = reader.ReadInt32();
        let r = []; //[OBState]
        for (let i = 0; i < cnt; i++) {
            let s = reader.ReadUInt32() * 4;
            let p = reader.pos;
            reader.pos = s;
            r.push(this.readState(reader));
            reader.pos = p;
        }
        return r;
    }

    readState(reader) {
        let data = this.data;
        let nameIdx = reader.ReadUInt32();
        let name = data.GetString(nameIdx);
        let variables = this.readVariables(reader);
        // 读取函数
        this.readFunctions(reader);
        // UMessageHandler[]
        let handlers = this.readHandlers(reader);
        // UEventHandler[]
        let ehandlers = this.readEHandlers(reader);
        // Dictionary<string, List<UMessageHandler>> Mh = new Dictionary<string, List<UMessageHandler>>();
        let Mh = {};
        for (let i = 0; i < handlers.length; i++) {
            let h = handlers[i];
            let hl = Mh[h.Name];
            if (hl) {
            } else {
                hl = []; //new List<UMessageHandler>();
                Mh[h.Name] = hl;
            }
            hl.push(h);
        }
        // Dictionary<string, UEventHandler> eh = new Dictionary<string, UEventHandler>();
        let eh = {};
        for (let i = 0; i < ehandlers.length; i++) {
            let h = ehandlers[i];
            eh[h.Name] = h;
        }
        let r = new OBState();
        r.Variables = variables;
        r.Name = name;
        r.MessageHandlers = Mh;
        r.EventHandlers = eh;
        return r;
    }

    readEHandlers(reader) {
        let cnt = reader.ReadInt32();
        // UEventHandler[] f = new UEventHandler[cnt];
        let f = [];
        for (let i = 0; i < cnt; i++) {
            let start = reader.ReadUInt32() * 4;
            let h = this.readEHandler(reader, start);
            f[i] = h;
        }
        return f;
    }

    readEHandler(reader, start) {
        let pos = reader.pos;
        reader.pos = start;
        let func = this.readFunction(reader);
        let h = new OBEventHandler();
        h.Name = func.Signure;
        h.Func = func;
        reader.pos = pos;
        return h;
    }

    readHandlers(reader) {
        let cnt = reader.ReadUInt32();
        // UMessageHandler[] f = new UMessageHandler[cnt];
        let f = [];
        for (let i = 0; i < cnt; i++) {
            let start = reader.ReadUInt32() * 4;
            let h = this.readHandler(reader, start);
            f[i] = h;
        }
        return f;
    }

    readHandler(reader, start) {
        let pos = reader.pos;
        reader.pos = start;
        let func = this.readFunction(reader);
        let h = new OBMessageHandler();
        let pair = func.Signure.split(':');
        h.Name = pair[0];
        h.Func = func;
        h.ArgTypeName = pair[1];
        reader.pos = pos;
        return h;
    }

    readVariables(reader) {
        let data = this.data;
        let varCnt = reader.ReadUInt32();
        // List<UVariableInfo> d = new List<UVariableInfo>();
        let d = [];
        for (let i = 0; i < varCnt; i++) {
            let v = this.readVariable(reader);
            d.push(v);
        }
        return d;
    }

    readVariable(reader) {
        let typeIdx = reader.ReadInt32();
        let count = reader.ReadInt32();
        let v = new OBVariableInfo(typeIdx, count);
        return v;
    }

    readFunctions(reader) {
        let cnt = reader.ReadInt32();
        let f = [];
        for (let i = 0; i < cnt; i++) {
            let start = reader.ReadUInt32() * 4;
            let pos = reader.pos;
            reader.pos = start;
            let _f = this.readFunction(reader);
            f[i] = _f;
            this.loadingFunctions[_f.Signure] = _f;
            reader.pos = pos;
        }
        return f;
    }

    readFunction(reader) {
        let builder = new OBFunctionBuilder(this);
        builder.loadFunctionHeader(reader);
        builder.loadStatement(reader);
        this.addLinking(builder);
        let f = builder.build();
        return f;
    }

    addLinking(l) {
        this.Linkings.push(l);
    }

    loadStructDataSegment() {
        let reader = this.reader;
        let data = this.data;
        return new OBStructDataReader().readStructData(reader, data);
    }
    /**
         *
         * @returns {Object.<string,OBStructValueData>}
         */
    loadStructDefDataSegment() {
        let reader = this.reader;
        let data = this.data;
        let length = reader.ReadInt32();
        let d = {};
        let cnt = reader.ReadInt32();
        for (let i = 0; i < cnt; i++) {
            let nameIdx = reader.ReadUInt32();
            let name = data.GetString(nameIdx);
            let typeCnt = reader.ReadUInt32();
            let structCnt = (typeCnt & 0x7F);
            let stringCnt = ((typeCnt >> 7) & 0x7F);
            let integerCnt = ((typeCnt >> 14) & 0x7F);
            let floatCnt = ((typeCnt >> 21) & 0x7F);
            let NobjectCnt = ((typeCnt >> 28));
            // string[] fields = new string[structCnt];
            let fields = [];
            for (let j = 0; j < structCnt; j++) {
                let fnameIdx = reader.ReadUInt32();
                let fname = data.GetString(fnameIdx);
                fields[j] = fname;
            }
            let s = new OBStructDef();
            s.Name = name;
            s.StructCnt = structCnt;
            s.StringCnt = stringCnt;
            s.IntegerCnt = integerCnt;
            s.FloatCnt = floatCnt;
            s.NobjectCnt = NobjectCnt;
            s.StructFields = fields;
            d[name] = s;
        }
        return d;
    }

    loadDataSegment() {
        let length = this.reader.ReadInt32();
        return this.reader.readSub(length * 4);
    }

    loadPackageInfo() {
        let reader = this.reader;
        let data = this.data;
        let depCnt = reader.ReadInt32();
        let err = [];
        for (let i = 0; i < depCnt; i++) {
            let nameIdx = reader.ReadInt32();
            let name = data.GetString(nameIdx);
            let hashIdx = reader.ReadInt32();
            let hash = data.GetString(hashIdx);
            let lhash = this.script.NativeLibHash[name];
            if (!lhash) {
                err.push("No native lib named " + name);
            } else if (lhash != hash) {
                err.push("Native lib hash mismatching." + name + " require " + hash + ", provide " + lhash);
            }
        }
        if (err.length > 0) {
            // throw err;
            console.error(err);
        }
    }
}

export class OBArrayBufferReader {
    /**
     * @type {Number} integer of position
     */
    pos; // int
    /**
     * @type {ArrayBuffer}
     */
    buf; // ArrayBuffer
    /**
     * @type DataView
     */
    view; // DataView
    /**
     * @type {Object.<Number,String>}
     */
    stringCache = {};

    constructor(buf) {
        this.buf = buf;
        this.pos = 0;
        this.view = new DataView(buf);
    }

    ReadInt32() {
        let v = this.getInt32(this.pos);
        this.pos += 4;
        return v;
    }

    getInt32(p) {
        let v = this.view.getInt32(p, true);
        return v;
    }

    ReadUInt32() {
        let v = this.getUint32(this.pos);
        this.pos += 4;
        return v;
    }

    getUint32(p) {
        let v = this.view.getUint32(p, true);
        return v;
    }

    ReadSingle() {
        let v = this.getFloat(this.pos);
        return v;
    }

    getFloat(p) {
        let v = this.view.getFloat32(p, true);
        return v;
    }

    GetString(stringIdx) {
        let start = stringIdx * 8; // 字符串是8字节对齐
        let length = this.view.getUint32(start, true);
        if (length === 0) {
            return "";
        }
        let ab = this.buf.slice(start + 4, start + 4 + length);
        let ui8 = new Uint8Array(ab);
        let utf8decoder = new util.TextDecoder("utf-8", {
            fatal: true
        });
        let str = utf8decoder.decode(ui8);
        if (str === null) {
            throw Error('no string value of idx:' + stringIdx);
        }
        return str;
    }

    readSub(length) {
        let v = this.getSub(this.pos, length);
        this.pos += length;
        return v;
    }

    getSub(pos, length) {
        let buf;
        if (typeof (length) === "undefined") {
            buf = this.buf.slice(pos);
        } else {
            buf = this.buf.slice(pos, pos + length);
        }
        return new OBArrayBufferReader(buf);
    }

    seek(pos) {
        if (typeof (pos) === "number") {
            this.pos = pos;
        }
        return this.pos;
    }
    /**
         *
         * @param {Number} startIdx
         * @returns {number[]}
         */
    GetInt32FromBin(startIdx) {
        let start = startIdx * 8; // 8字节对齐
        let p = this.pos;
        this.pos = start;
        let byteLength = this.ReadUInt32();
        let length = byteLength / 4;
        let r = [];
        for (let i = 0; i < length; i++) {
            r.push(this.ReadInt32());
        }
        this.pos = p;
        return r;
    }
}


export class OBStatementContext {
    InstPos;
    Actions = [];

    PushAction(Instruction) {
        this.Actions.push(Instruction);
    }
}
// 虚拟机

export class VMInterruptException {
}
export class ChangeStateException extends VMInterruptException {
}
export class ChangeDestroyException extends VMInterruptException {
}
export class OBVM {
    /**
     * @1type {function(any)}
     */
    Output;
    /**
     * typeName->[VMFSM]
     * @1type {Object.<string:OBVMFSM[]>}
     */
    Running = {};
    /**
     * @type OBScript
     */
    script;
    /**
     * @1type {OBVMFSM}
     */
    Pending = [];
    /**
     *
     * @param {OBScript} script
     */
    constructor(script) {
        if (!script) {
            throw Error("Script is null");
        }
        this.script = script;
    }

    CreateFSM(name) {
        if (name == null) {
            return null;
        }
        if (this.script == null) {
            throw Error("Script is null");
        }
        let fsmdata = this.script.FullNameFSMData[name];
        if (!fsmdata) {
            return null;
        }
        let uBFSM = new OBVMFSM(this, fsmdata);
        let list = this.Running[name];
        if (!list) {
            list = [];
            this.Running[name] = list;
        }
        list.push(uBFSM);
        return uBFSM;
    }

    update() {
        this._HandleOnePendingFSM();
        let timestamp = Date.now();
        // JS不需要在VM中处理计划任务
        // this._HandleSchedulingTask(timestamp);
        // this._InvokeScheduledTask(timestamp);
    }

    _HandleOnePendingFSM() {
        while (this.Pending.length > 0) {
            let fsm = this.Pending.shift();
            if (fsm) {
                fsm.HandleAllMessages();
            }
        }
    }
    /**
         *
         * @param {OBVMFSM} fsm
         */
    _AddPendingFSM(fsm) {
        this.Pending.push(fsm);
    }

    Log(v) {
        console.log(v);
        if (this.Output) {
            this.Output(v);
        }
    }
    /**
         *
         * @param {OBVMFSM} fsm
         */
    DestroyFSM(fsm) {
        let name = fsm.data.FullName;
        let list = this.Running[name];
        if (list) {
            let idx = list.findIndex((f) => f === fsm);
            if (idx > -1) {
                list.splice(idx, 1);
            }
        }
    }
    /**
         *
         * @param {OBUserMessage} userMessage
         */
    BroadcastMessage(userMessage) {
        Object.values(this.Running).forEach(l => {
            for (let i = 0; i < l.length; i++) {
                let f = l[i];
                if (f && f != userMessage.sender) {
                    f.PostMessage(userMessage);
                }
            }
        });
    }
    /**
         *
         * @param {number} millisecond wait time
         * @param {*} callback
         */
    Schedule(millisecond, callback) {
        setTimeout(callback, millisecond, this);
    }

    FindRunningFSMByType(typeFullName) {
        return this.Running[typeFullName] || [];
    }
}
export class OBVMFSM {
    static ID_GEN = 0;
    /**
     * @type {any}
     */
    Target;
    /**
     * @type {OBFSM}
     */
    data;
    id;
    /**
     * @type {OBVMState}
     */
    CurrentState;
    /**
     * @type {OBVMState[]}
     */
    StateStack = [];
    /**
     * @type {OBVM}
     */
    VM;
    Inbox = [];
    PrioritizedInbox = [];
    VariableGroup;
    /**
     *
     * @param {OBVM} vm
     * @param {OBFSM} data
     */
    constructor(vm, data) {
        this.data = data;
        this.id = ++OBVMFSM.ID_GEN;
        this.VM = vm;
        this.VariableGroup = new OBTypedVariableGroup(data.Variables);
        this.CurrentState = new OBVMState(data.Entry, this);
        this.PostPrioritizedMessage(new OBEventMessage("Start", "", null, null));
    }
    /**
         * 推送高优先级消息
         * @param {OBMessage} msg
         */
    PostPrioritizedMessage(msg) {
        if (this.PrioritizedInbox == null) {
            return;
        }
        this.PrioritizedInbox.push(msg);
        this.VM._AddPendingFSM(this);
    }
    /**
         * 推送消息
         * @param {OBMessage} msg
         */
    PostMessage(msg) {
        if (this.Inbox == null) {
            return;
        }
        this.Inbox.push(msg);
        this.VM._AddPendingFSM(this);
    }

    HandleAllMessages() {
        let msg;
        while (msg = this.PrioritizedInbox.shift()) {
            msg.Handle(this.CurrentState);
        }
        while (msg = this.Inbox.shift()) {
            msg.Handle(this.CurrentState);
            while (msg = this.PrioritizedInbox.shift()) {
                msg.Handle(this.CurrentState);
            }
        }
    }

    Destroy() {
        this.VariableGroup = null;
        this.CurrentState = null;
        this.Inbox.length = 0;
        this.StateStack.length = 0;
        this.PrioritizedInbox.length = 0;
        this.VM.DestroyFSM(this);
    }
    /**
         *
         * @param {string} title
         * @returns bool
         */
    IsListeningEvent(title) {
        return this.CurrentState.IsListeningEvent(title);
    }

    toString() {
        return "FSM:" + this.data.FullName;
    }

    ChangeState(name) {

        if (this.VM == null) {
            return;
        }
        if (this.data.States[name]) {
            this.CurrentState = new OBVMState(this.data.States[name], this);
            this.PostPrioritizedMessage(new OBEventMessage("Start", null, null, this));
        } else {
            throw Error("No state named " + name + " of FSM " + this.data.Name);
        }
    }
}
export class OBTypedVariableGroup {
    LongRegister;
    DoubleRegister;
    StringRegister;
    StructRegister;
    NObjectRegister;
    /**
     *
     * @param {OBVariableInfo[]} variables
     */
    constructor(variables) {
        if (!variables) {
            return;
        }
        variables.forEach(v => {
            switch (v.typeIdx) {
                case 0:
                    if (this.LongRegister != null) {
                        throw Error("duplicated type " + v.typeIdx);
                    }
                    this.LongRegister = [];
                    this.LongRegister.length = v.count;
                    this.LongRegister.fill(0);
                    break;
                case 1:
                    if (this.DoubleRegister != null) {
                        throw Error("duplicated type " + v.typeIdx);
                    }
                    this.DoubleRegister = [];
                    this.DoubleRegister.length = v.count;
                    this.DoubleRegister.fill(0);
                    break;
                case 2:
                    if (this.StringRegister != null) {
                        throw Error("duplicated type " + v.typeIdx);
                    }
                    this.StringRegister = [];
                    this.StringRegister.length = v.count;
                    this.StringRegister.fill("");
                    break;

                case 3:
                    if (this.StructRegister != null) {
                        throw Error("duplicated type " + v.typeIdx);
                    }
                    this.StructRegister = [];
                    this.StructRegister.length = v.count;
                    break;
                case 4:
                    if (this.NObjectRegister != null) {
                        throw Error("duplicated type " + v.typeIdx);
                    }
                    this.NObjectRegister = [];
                    this.NObjectRegister.length = v.count;
                    break;
                default:
                    throw Error("Unknown type " + v.typeIdx);
            }
        });
    }
}
export class OBVMState {
    /**
     * @type {OBState}
     */
    data;
    /**
     * @type {OBVMFSM}
     */
    fsm;
    /**
     * @type {OBTypedVariableGroup}
     */
    VariableGroup;
    /**
     * @type {OBMessage}
     */
    currentMessage;

    constructor(data, fsm) {
        this.data = data;
        this.VariableGroup = new OBTypedVariableGroup(data.Variables);
        this.fsm = fsm;
    }
    /**
         *
         * @param {OBMessage} msg
         */
    HandleEvent(msg) {
        try {
            let h = this.data.EventHandlers[msg.name];
            if (h) {
                this.currentMessage = msg;
                new OBVMFunction(h.Func).Call(this);
            }
        } finally {
            this.currentMessage = null;
        }
    }

    HandleMessage(m) {
        this.currentMessage = m;
        try {
            let typeName = null;
            if (m.arg != null) {
                typeName = m.GetArgType();
            }
            let hl = this.data.MessageHandlers[m.name];
            if (hl) {
                hl.forEach(h => {
                    if (h.ArgTypeName === "" || h.ArgTypeName === typeName) {
                        new OBVMFunction(h.Func).Call(this);
                    }
                });
            }
        } finally {
            this.currentMessage = null;
        }
    }
    /**
         *
         * @param {string} title
         * @returns bool
         */
    IsListeningEvent(title) {
        return !!this.data.EventHandlers[title];
    }

    ReceivedMessage() {
        if (this.currentMessage) {
            return this.currentMessage.arg;
        } else {
            throw Error("当前上下文没有消息可用");
        }
    }

    CurrentMessageSender() {
        if (this.currentMessage) {
            return this.currentMessage.sender;
        } else {
            throw Error("当前上下文没有消息可用");
        }
    }
}
export class OBVMFunction {
    /**
     * @type {OBFunction}
     */
    data;
    /**
     * @type {OBTypedVariableGroup}
     */
    LocalVar;
    returnType = -1;
    returnValue;
    LongRegister;
    DoubleRegister;
    StringRegister;
    StructRegister;
    NObjectRegister;
    /**
     *
     * @param {OBFunction} obfunc
     */
    constructor(obfunc, builder, args) {
        this.data = obfunc;
        let LocalVar = new OBTypedVariableGroup(obfunc.Variables);
        this.LocalVar = LocalVar;
        if (builder) {
            /**
             * @1type {List<Func<UBState, UBFunction, TypedVariableGroup, long>>();}
             */
            let LongRegister_ = [];
            let DoubleRegister_ = [];// new List<Func<UBState, UBFunction, TypedVariableGroup, double>>();
            let StringRegister_ = [];// new List<Func<UBState, UBFunction, TypedVariableGroup, string>>();
            let StructRegister_ = [];// new List<Func<UBState, UBFunction, TypedVariableGroup, AStruct>>();
            let NObjectRegister_ = [];//new List<Func<UBState, UBFunction, TypedVariableGroup, object>>();

            for (let i = 1; i < args.length; i++) {
                let arg = args[i];
                let Register = arg & 0xFFF;
                let RegisterType = (arg >> 12) & 0xF;

                switch (RegisterType) {
                    case 0:
                        LongRegister_.push(builder.LongRegister[Register]);
                        break;
                    case 1:
                        DoubleRegister_.push(builder.DoubleRegister[Register]);
                        break;
                    case 2:
                        StringRegister_.push(builder.StringRegister[Register]);
                        break;
                    case 3:
                        StructRegister_.push(builder.StructRegister[Register]);
                        break;
                    case 4:
                        NObjectRegister_.push(builder.NObjectRegister[Register]);
                        break;
                    default:
                        throw Error("Unknown type " + RegisterType);
                }
            }
            if (LongRegister_.length > 0) {
                this.LongRegister = LongRegister_;
            }
            if (DoubleRegister_.length > 0) {
                this.DoubleRegister = DoubleRegister_;
            }
            if (StringRegister_.length > 0) {
                this.StringRegister = StringRegister_;
            }
            if (StructRegister_.length > 0) {
                this.StructRegister = StructRegister_;
            }
            if (NObjectRegister_.length > 0) {
                this.NObjectRegister = NObjectRegister_;
            }
        }
    }
    /**
         *
         * @param {OBVMState} state
         */
    Call(state, uBFunction, localVars) {
        if (uBFunction) {
            if (this.LongRegister != null) {
                for (let i = 0; i < this.LongRegister.length; i++) {
                    this.LocalVar.LongRegister[i] = this.LongRegister[i](state, uBFunction, localVars);
                }
            }
            if (this.DoubleRegister != null) {
                for (let i = 0; i < this.DoubleRegister.length; i++) {
                    this.LocalVar.DoubleRegister[i] = this.DoubleRegister[i](state, uBFunction, localVars);
                }
            }
            if (this.StringRegister != null) {
                for (let i = 0; i < this.StringRegister.length; i++) {
                    this.LocalVar.StringRegister[i] = this.StringRegister[i](state, uBFunction, localVars);
                }
            }
            if (this.StructRegister != null) {
                for (let i = 0; i < this.StructRegister.length; i++) {
                    this.LocalVar.StructRegister[i] = this.StructRegister[i](state, uBFunction, localVars);
                }
            }
            if (this.NObjectRegister != null) {
                for (let i = 0; i < this.NObjectRegister.length; i++) {
                    this.LocalVar.NObjectRegister[i] = this.NObjectRegister[i](state, uBFunction, localVars);
                }
            }
        }
        let Actions = this.data.Statements.Actions;
        for (let i = 0; i < Actions.length && i >= 0;) {
            let action = Actions[i];
            i = action(state, this, this.LocalVar, i);
        }
    }

    SetReturnLong(v) {
        this.returnType = 1;
        this.returnValue = v;
    }

    Long() {
        if (this.returnType === 1) {
            return this.returnValue;
        }
        else {
            throw Error(this.data.Signure + " 没有返回 long 类型:" + this.returnType);
        }
    }

    SetReturnDouble(v) {
        this.returnType = 2;
        this.returnValue = v;
    }

    Double() {
        if (this.returnType === 2) {
            return this.returnValue;
        }
        else {
            throw Error(this.data.Signure + " 没有返回 double 类型:" + this.returnType);
        }
    }

    SetReturnString(v) {
        this.returnType = 3;
        this.returnValue = v;
    }

    String() {
        if (this.returnType === 3) {
            return this.returnValue;
        }
        else {
            throw Error(this.data.Signure + " 没有返回 string 类型:" + this.returnType);
        }
    }

    SetReturnStruct(v) {
        this.returnType = 4;
        this.returnValue = v;
    }

    Struct() {
        if (this.returnType === 4) {
            return this.returnValue;
        }
        else {
            throw Error(this.data.Signure + " 没有返回 Struct 类型:" + this.returnType);
        }
    }

    SetReturnNObject(v) {
        this.returnType = 5;
        this.returnValue = v;
    }

    NObject() {
        if (this.returnType === 5) {
            return this.returnValue;
        }
        else {
            throw Error(this.data.Signure + " 没有返回 NObject 类型:" + this.returnType);
        }
    }
}
export class OBMessage {
    name;
    arg;
    argType;
    sender;
    /**
     *
     * @param {string} name
     * @param {string} argType
     * @param {any} arg
     * @param {?OBVMFSM} sender
     */
    constructor(name, argType, arg, sender) {
        this.name = name;
        this.argType = argType;
        this.arg = arg;
        this.sender = sender;
    }

    GetArgType() {
        return this.argType;
    }

    static ArgTypeOf(typeId, arg) {
        switch (typeId) {
            case 0xf:
                return "";
            case 0:
                return "Integer";
            case 1:
                return "Number";
            case 2:
                return "String";
            case 3:
                return arg.Def.Name;
            case 4:
                // return "NObject";
                if (arg.constructor === OBVMFSM) {
                    return "FSM";
                } else {
                    if (arg.constructor) {
                        return arg.constructor.name;
                    } else {
                        return typeof (arg);
                    }
                }
            default:
                throw Error("Unknown type:" + typeId);
        }
    }
}
export class OBEventMessage extends OBMessage {
    /**
     *
     * @param {OBVMState} state
     */
    Handle(state) {
        try {
            state.HandleEvent(this);
        } catch (err) {
            if (!(err instanceof VMInterruptException)) {
                console.error(err);
                throw err;
            }
        }
    }
}
export class OBUserMessage extends OBMessage {

    /**
     *
     * @param {OBVMState} state
     */
    Handle(state) {
        try {
            state.HandleMessage(this);
        } catch (err) {
            if (!(err instanceof VMInterruptException)) {
                console.error(err);
                throw err;
            }
        }
    }
}
// 字节码
export class STMT_start extends OBInstruction {
    StatementContext;

    init(code, builder, instructions, i) {
        this.StatementContext = new OBStatementContext();
        this.StatementContext.InstPos = this.Position;
    }

    link(builder, instructions, i) {
        if (builder.CurrentStatementStack.length > 0) {
            let Actions = null;
            builder.PushAction((st, uf, locals, pos) => {
                if (Actions == null) {
                    Actions = this.StatementContext.Actions;
                }
                for (let i = 0; i < Actions.length;) {
                    let action = Actions[i];
                    i = action(st, uf, locals, i);
                }
                return pos + 1;
            });
        }
        builder.CurrentStatementStack.push(this.StatementContext);
    }
}
export class LDSTR extends OBInstruction {
    Value;
    Register;

    init(code, builder, instructions, i) {
        let stridx = code & 0xFFF;
        this.Value = builder.loader.data.GetString(stridx);
        this.Register = (code & 0xFF0000) >> 16;
    }

    link(builder, instructions, idx) {
        builder.StringRegister[this.Register] = this.getValue.bind(this);
    }

    getValue(UBState, obvmfunction, TypedRegisters) {
        return this.Value;
    }
}
export class PRT extends OBInstruction {
    RegisterType;
    RegisterIdx;

    init(code, builder, instructions, i) {
        this.RegisterType = (code & 0xF00000) >> 20;
        this.RegisterIdx = code & 0xFFFFF;
    }

    link(builder, instructions, idx) {
        let v;
        switch (this.RegisterType) {
            case 0:
                v = builder.LongRegister[this.RegisterIdx];
                break;
            case 1:
                v = builder.DoubleRegister[this.RegisterIdx];
                break;
            case 2:
                v = builder.StringRegister[this.RegisterIdx];
                break;
            case 3:
                v = builder.StructRegister[this.RegisterIdx];
                break;
            case 4:
                v = builder.NObjectRegister[this.RegisterIdx];
                break;
            default:
                throw Error("Unknown type:" + this.RegisterType);
        }
        builder.PushAction((st, uf, locals, pos) => {
            //Console.WriteLine(n(st, uf));
            let val = v(st, uf, locals);
            let vm = st.fsm.VM;
            vm.Log(val);
            return ++pos;
        });
    }
}
export class B_STMT_end extends OBInstruction {
    link(builder, instructions, idx) {
        builder.PushAction((st, uf, locals, pos) => {
            return pos + 1;
        });
        let sc = builder.CurrentStatementStack.pop();
        if (builder.CurrentStatementStack.length === 0) {
            builder.RootStatementContext = sc;
        }
    }
}
export class ReceivedMessage extends OBInstruction {
    typeId;
    Register;

    init(code, builder, instructions, i) {
        this.typeId = (code >> 20) & 0xf;
        this.Register = code & 0xfffff;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        switch (this.typeId) {
            case 0:
                builder.LongRegister[Register] = (st, uf, locals) => {
                    return st.ReceivedMessage();
                };
                break;
            case 1:
                builder.DoubleRegister[Register] = (st, uf, locals) => {
                    return st.ReceivedMessage();
                };
                break;
            case 2:
                builder.StringRegister[Register] = (st, uf, locals) => {
                    return st.ReceivedMessage();
                };
                break;
            case 3:
                builder.StructRegister[Register] = (st, uf, locals) => {
                    return st.ReceivedMessage();
                };
                break;
            case 4:
                builder.NObjectRegister[Register] = (st, uf, locals) => {
                    return st.ReceivedMessage();
                };
                break;
            default:
                throw Error("Unknown type " + this.typeId);
        }
    }
}
export class STVS extends OBInstruction {
    Register;
    VarIdx;
    RegisterType;

    init(code, builder, instructions, i) {
        this.Register = (code & 0xFF00) >> 8;
        this.RegisterType = (code & 0xFF0000) >> 16;
        this.VarIdx = code & 0xFF;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        let VarIdx = this.VarIdx;
        let RegisterType = this.RegisterType;
        switch (RegisterType) {
            case 0:
                let fl = builder.LongRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.VariableGroup.LongRegister[VarIdx] = fl(st, uf, locals);
                    return ++pos;
                });
                break;
            case 1:
                let fd = builder.DoubleRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.VariableGroup.DoubleRegister[VarIdx] = fd(st, uf, locals);
                    return ++pos;
                });
                break;
            case 2:
                let fs = builder.StringRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.VariableGroup.StringRegister[VarIdx] = fs(st, uf, locals);
                    return ++pos;
                });
                break;
            case 3:
                let fst = builder.StructRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    let stt = fst(st, uf, locals);
                    st.VariableGroup.StructRegister[VarIdx] = stt;
                    if (!stt) {
                        debugger
                    }
                    return ++pos;
                });
                break;
            case 4:
                let fo = builder.NObjectRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.VariableGroup.NObjectRegister[VarIdx] = fo(st, uf, locals);
                    return ++pos;
                });
                break;
            default:
                throw Error("Unknown type " + RegisterType);
        }
    }
}
export class STVG extends OBInstruction {
    Register;
    VarIdx;
    RegisterType;

    init(code, builder, instructions, i) {
        this.Register = (code & 0xFF00) >> 8;
        this.RegisterType = (code & 0xFF0000) >> 16;
        this.VarIdx = code & 0xFF;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        let VarIdx = this.VarIdx;
        switch (this.RegisterType) {
            case 0:
                builder.LongRegister[Register] = (st, uf, locals) => {
                    return st.VariableGroup.LongRegister[VarIdx];
                };
                break;
            case 1:
                builder.DoubleRegister[Register] = (st, uf, locals) => {
                    return st.VariableGroup.DoubleRegister[VarIdx];
                };
                break;
            case 2:
                builder.StringRegister[Register] = (st, uf, locals) => {
                    return st.VariableGroup.StringRegister[VarIdx];
                };
                break;
            case 3:
                builder.StructRegister[Register] = (st, uf, locals) => {
                    return st.VariableGroup.StructRegister[VarIdx];
                };
                break;
            case 4:
                builder.NObjectRegister[Register] = (st, uf, locals) => {
                    return st.VariableGroup.NObjectRegister[VarIdx];
                };
                break;
            default:
                throw Error("Unknown type " + this.RegisterType);
        }
    }
}
export class StructFieldDesc extends OBInstruction {
    fieldTypeId;
    fieldDescIdx;

    init(code, builder, instructions, i) {
        this.fieldDescIdx = code & 0xfffff;
        this.fieldTypeId = (code >> 20) & 0xf;
    }
}
export class GetStructField extends OBInstruction {
    structIdx;
    Register;
    typeId;
    fieldIdx;

    init(code, builder, instructions, i) {
        let desc = instructions[i - 1];
        if (!(desc instanceof StructFieldDesc)) {
            throw Error("last cmd is not StructFieldDesc");
        }
        this.structIdx = (code >> 12) & 0xfff;
        this.Register = (code) & 0xfff;
        this.typeId = desc.fieldTypeId;
        this.fieldIdx = desc.fieldDescIdx;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        let fieldIdx = this.fieldIdx;
        let getStruct = builder.StructRegister[this.structIdx];
        switch (this.typeId) {

            case 0:
                builder.LongRegister[Register] = (st, uf, locals) => {
                    let stt = getStruct(st, uf, locals);
                    return stt.registers.LongRegister[fieldIdx];
                };
                break;
            case 1:
                builder.DoubleRegister[Register] = (st, uf, locals) => {
                    return getStruct(st, uf, locals).registers.DoubleRegister[fieldIdx];
                };
                break;
            case 2:
                builder.StringRegister[Register] = (st, uf, locals) => {
                    return getStruct(st, uf, locals).registers.StringRegister[fieldIdx];
                };
                break;
            case 3:
                builder.StructRegister[Register] = (st, uf, locals) => {
                    return getStruct(st, uf, locals).registers.StructRegister[fieldIdx];
                };
                break;
            case 4:
                builder.NObjectRegister[Register] = (st, uf, locals) => {
                    return getStruct(st, uf, locals).registers.NObjectRegister[fieldIdx];
                };
                break;
            default:
                throw Error("Unknown type " + this.typeId);
        }

    }
}
export class SetStructField extends OBInstruction {
    structIdx;
    Register;
    typeId;
    fieldIdx;

    init(code, builder, instructions, i) {
        let desc = instructions[i - 1];
        if (!(desc instanceof StructFieldDesc)) {
            throw Error("last cmd is not StructFieldDesc");
        }
        this.structIdx = (code >> 12) & 0xfff;
        this.Register = (code) & 0xfff;
        this.typeId = desc.fieldTypeId;
        this.fieldIdx = desc.fieldDescIdx;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        let fieldIdx = this.fieldIdx;
        let getStruct = builder.StructRegister[this.structIdx];
        switch (this.typeId) {
            case 0:
                let getLong = builder.LongRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    getStruct(st, uf, locals).registers.LongRegister[fieldIdx] = getLong(st, uf, locals);
                    return ++pos;
                });
                break;
            case 1:
                let getDouble = builder.DoubleRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    getStruct(st, uf, locals).registers.DoubleRegister[fieldIdx] = getDouble(st, uf, locals);
                    return ++pos;
                });
                break;
            case 2:
                let GetString = builder.StringRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    getStruct(st, uf, locals).registers.StringRegister[fieldIdx] = GetString(st, uf, locals);
                    return ++pos;
                });
                break;
            case 3:
                let getStruct1 = builder.StructRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    getStruct(st, uf, locals).registers.StructRegister[fieldIdx] = getStruct1(st, uf, locals);
                    return ++pos;
                });
                break;
            case 4:
                let getNObject = builder.NObjectRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    getStruct(st, uf, locals).registers.NObjectRegister[fieldIdx] = getNObject(st, uf, locals);
                    return ++pos;
                });
                break;
            default:
                throw Error("Unknown type " + this.typeId);
        }

    }
}
export class CHSTT extends OBInstruction {
    StateName;

    init(code, builder, instructions, i) {
        let strIdx = (code & 0xFFFFFF);
        let str = builder.loader.data.GetString(strIdx);
        this.StateName = str;
    }

    link(builder, instructions, idx) {
        builder.PushAction((state, uf, locals, pos) => {
            state.fsm.ChangeState(this.StateName);
            throw new ChangeStateException();
            //return ++pos;
        });
    }
}
export class MethodCallRegisterInfoAnchor extends OBInstruction {
    RegisterInfoIdx;

    init(code, builder, instructions, i) {
        this.RegisterInfoIdx = code & 0xFFFF;
    }
}
export class NativeMethodCall extends OBInstruction {
    LibNameIdx;

    init(code, builder, instructions, i) {
        this.LibNameIdx = code & 0xFFFFFF;
    }

    link(builder, instructions, idx) {
        let LibName = builder.loader.data.GetString(this.LibNameIdx);
        let anchor = instructions[idx - 1];
        if (!(anchor instanceof MethodCallRegisterInfoAnchor)) {
            throw Error("last cmd is not MethodCallRegisterInfoAnchor");
        }
        let RegisterInfoIdx = anchor.RegisterInfoIdx;
        let args = builder.loader.data.GetInt32FromBin(RegisterInfoIdx);
        let funcIdx = args[0];
        let _args = args.slice(1);
        let installer = builder.loader.script.getNativeFunc(LibName, funcIdx);
        installer(builder, _args);
    }
}
export class FSMVS extends OBInstruction {
    Register;
    VarIdx;
    RegisterType;

    init(code, builder, instructions, i) {
        this.Register = (code & 0xFF00) >> 8;
        this.RegisterType = (code & 0xFF0000) >> 16;
        this.VarIdx = code & 0xFF;
    }

    link(builder, instructions, idx) {
        let Register = this.Register;
        let VarIdx = this.VarIdx;
        let RegisterType = this.RegisterType;
        switch (RegisterType) {
            case 0:
                let fl = builder.LongRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.fsm.VariableGroup.LongRegister[VarIdx] = fl(st, uf, locals);
                    return ++pos;
                });
                break;
            case 1:
                let fd = builder.DoubleRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.fsm.VariableGroup.DoubleRegister[VarIdx] = fd(st, uf, locals);
                    return ++pos;
                });
                break;
            case 2:
                let fs = builder.StringRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.fsm.VariableGroup.StringRegister[VarIdx] = fs(st, uf, locals);
                    return ++pos;
                });
                break;
            case 3:
                let fst = builder.StructRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.fsm.VariableGroup.StructRegister[VarIdx] = fst(st, uf, locals);
                    return ++pos;
                });
                break;
            case 4:
                let fo = builder.NObjectRegister[Register];
                builder.PushAction((st, uf, locals, pos) => {
                    st.fsm.VariableGroup.NObjectRegister[VarIdx] = fo(st, uf, locals);
                    return ++pos;
                });
                break;
            default:
                throw Error("Unknown type " + RegisterType);
        }
    }
}
export class SLF extends OBInstruction {
    RegisterIdx;

    init(code, builder, instructions, i) {
        this.RegisterIdx = code & 0xffffff;
    }

    link(builder, instructions, idx) {
        builder.NObjectRegister[this.RegisterIdx] = (s, f, l) => {
            return s.fsm;
        };
    }
}
export class MethodCall extends OBInstruction {
    MethodNameIdx;

    init(code, builder, instructions, i) {
        this.MethodNameIdx = code & 0xFFFFFF;
    }

    link(builder, instructions, idx) {
        let MethodName = builder.loader.data.GetString(this.MethodNameIdx);
        let anchor = instructions[idx - 1];
        if (!(anchor instanceof MethodCallRegisterInfoAnchor)) {
            throw Error("last cmd is not MethodCallRegisterInfoAnchor");
        }
        let RegisterInfoIdx = anchor.RegisterInfoIdx;
        let args = builder.loader.data.GetInt32FromBin(RegisterInfoIdx);

        let uf1 = builder.loader.script.loadedFunctions[MethodName];
        if (uf1 != null) {
            // 参数
            let f = new OBVMFunction(uf1, builder, args);
            let returnRegister = args[0];
            if (returnRegister === -1) {
                builder.PushAction((state, uf, localVars, pos) => {
                    f.Call(state, f, localVars);
                    return ++pos;
                });
            } else {
                let Register = returnRegister & 0xFFF;
                let registerType = (returnRegister >> 12) & 0xF;
                // 处理有返回值的情况
                switch (registerType) {
                    case 0:
                        builder.LongRegister[Register] = (st, uf, l) => {
                            f.Call(st, uf, l);
                            return f.Long();
                        };
                        break;
                    case 1:
                        builder.DoubleRegister[Register] = (st, uf, l) => {
                            f.Call(st, uf, l);
                            return f.Double();
                        };
                        break;
                    case 2:
                        builder.StringRegister[Register] = (st, uf, l) => {
                            f.Call(st, uf, l);
                            return f.String();
                        };
                        break;
                    case 3:
                        builder.StructRegister[Register] = (st, uf, l) => {
                            f.Call(st, uf, l);
                            return f.Struct();
                        };
                        break;
                    case 4:
                        builder.NObjectRegister[Register] = (st, uf, l) => {
                            f.Call(st, uf, l);
                            return f.NObject();
                        };
                        break;
                    default:
                        throw Error("Unknown type " + registerType);
                }
            }
        } else {
            throw Error("未找到函数 " + MethodName);
        }
    }
}
export class BRIFN extends OBInstruction {
    checkRegType;
    checkRegIdx;
    targetOffset;

    init(code, builder, instructions, i) {
        this.checkRegType = ((code >> 20) & 0xf);
        this.checkRegIdx = ((code >> 14) & 0x7f);
        this.targetOffset = (((code & 0x1fff) << 19) >> 19); //;// ((code) & 0x1fff);
        builder.PositionUpdate(this.targetOffset * 4, (newPos) => {
            this.targetOffset = newPos;
        });
    }

    link(builder, instructions, idx) {
        let checkRegType = this.checkRegType;
        let checkRegIdx = this.checkRegIdx;
        switch (checkRegType) {
            case 0:
                let LongReg = builder.LongRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (LongReg(st, uf, locals) === 0) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 1:
                let DoubleReg = builder.DoubleRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (DoubleReg(st, uf, locals) === 0) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 2:
                let StringReg = builder.StringRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    let str = StringReg(st, uf, locals);
                    if (str == null || ("" === (str))) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 3:
                let StructReg = builder.StructRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (StructReg(st, uf, locals) == null) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 4:
                let NObjectReg = builder.NObjectRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (NObjectReg(st, uf, locals) == null) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            default:
                throw Error("Unknown type " + checkRegType);
        }
    }
}
export class BR extends OBInstruction {
    Offset;

    init(code, builder, instructions, i) {
        this.Offset = ((code << 8) >> 8);
        builder.PositionUpdate(this.Offset * 4, (newPos) => {
            this.Offset = newPos;
        });
    }

    link(builder, instructions, idx) {
        builder.PushAction((ub, uf, locals, pos) => {
            return this.Offset;
        });
    }
}
export class NOP extends OBInstruction {
    link(builder, instructions, idx) {
        builder.PushAction((st, uf, locals, pos) => {
            return pos + 1;
        });
    }
}
export class ARITHF extends OBInstruction {
    Opcode;
    LeftRegister;
    RightRegister;

    init(code, builder, instructions, i) {
        this.LeftRegister = (code >> 10) & 0x3FF;
        this.RightRegister = (code & 0x3FF);
        this.Opcode = (code >> 20) & 0xf;
    }

    link(builder, instructions, idx) {
        let left = builder.DoubleRegister[this.LeftRegister];
        let right = builder.DoubleRegister[this.RightRegister];
        if (left == null) {
            throw Error("left is null");
        }
        if (right == null) {
            throw Error("right is null");
        }
        let o;
        switch (this.Opcode) {
            case 0:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l + r;
                }
                break;
            case 1:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l - r;
                }
                break;
            case 2:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l * r;
                }
                break;
            case 3:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l / r;
                }
                break;
            case 4:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return Math.pow(l, r);
                }
                break;
            case 5:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l % r;
                }
                break;
            default:
                throw Error("未知操作符 " + this.Opcode);
        }
        builder.DoubleRegister[this.LeftRegister] = o;
    }
}

export class ARITHI extends OBInstruction {
    Opcode;
    LeftRegister;
    RightRegister;

    init(code, builder, instructions, i) {
        this.LeftRegister = (code >> 10) & 0x3FF;
        this.RightRegister = (code & 0x3FF);
        this.Opcode = (code >> 20) & 0xf;
    }

    link(builder, instructions, idx) {
        let left = builder.LongRegister[this.LeftRegister];
        let right = builder.LongRegister[this.RightRegister];
        if (left == null) {
            throw Error("left is null");
        }
        if (right == null) {
            throw Error("right is null");
        }
        let o;
        switch (this.Opcode) {
            case 0:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l + r;
                }
                break;
            case 1:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l - r;
                }
                break;
            case 2:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l * r;
                }
                break;
            case 3:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return Math.floor(l / r);
                }
                break;
            case 4:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return Math.floor(Math.pow(l, r));
                }
                break;
            case 5:
                o = (st, f, locals) => {
                    var l = left(st, f, locals);
                    var r = right(st, f, locals);
                    return l % r;
                }
                break;
            default:
                throw Error("未知操作符 " + this.Opcode);
        }
        builder.LongRegister[this.LeftRegister] = o;
    }
}
export class LDI extends OBInstruction {
    Register;
    Value;

    init(code, builder, instructions, i) {
        let pos = code & 0xFFF;
        this.Value = builder.loader.data.getInt32(pos * 4); // 4字节对齐
        this.Register = ((code & 0xFF0000) >> 16);
    }

    link(builder, instructions, idx) {
        builder.LongRegister[this.Register] = () => {
            return this.Value;
        };
    }
}
export class LDF extends OBInstruction {
    Register;
    Value;

    init(code, builder, instructions, i) {
        let specal = code & 0xFFFF;
        switch (specal) {
            case 0xFFFE:
                this.Value = Number.POSITIVE_INFINITY;
                break;
            case 0xFFFD:
                this.Value = Number.NEGATIVE_INFINITY;
                break;
            case 0xFFFF:
                this.Value = Number.NaN;
                break;
            default:
                this.Value = builder.loader.data.getFloat(specal * 4);
                break;
        }
        this.Register = ((code & 0xFF0000) >> 16);
    }

    link(builder, instructions, idx) {
        builder.DoubleRegister[this.Register] = () => {
            return this.Value;
        };
    }
}
export class RET extends OBInstruction {
    RegisterType;
    RegisterIdx;

    init(code, builder, instructions, i) {
        this.RegisterType = ((code & 0xF00000) >> 20);
        this.RegisterIdx = (code & 0xFFFFF);
    }

    link(builder, instructions, idx) {
        switch (this.RegisterType) {
            case 0:
                let l = builder.LongRegister[this.RegisterIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    let v = l(st, uf, locals);
                    uf.SetReturnLong(v);
                    return -1;
                });
                break;
            case 1:
                let f = builder.DoubleRegister[this.RegisterIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    let v = f(st, uf, locals);
                    uf.SetReturnDouble(v);
                    return -1;
                });
                break;
            case 2:
                let s = builder.StringRegister[this.RegisterIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    //Console.WriteLine(s(st, uf));
                    let v = s(st, uf, locals);
                    uf.SetReturnString(v);
                    return -1;
                });
                break;
            case 3:
                let u = builder.StructRegister[this.RegisterIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    //Console.WriteLine(u(st, uf));
                    let v = u(st, uf, locals);
                    uf.SetReturnStruct(v);
                    return -1;
                });
                break;
            case 4:
                let n = builder.NObjectRegister[this.RegisterIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    //Console.WriteLine(n(st, uf));
                    let v = n(st, uf, locals);
                    uf.SetReturnNObject(v);
                    return -1;
                });
                break;
            case 0xf:
                builder.PushAction((st, uf, locals, pos) => {
                    return -1;
                });
                break;
            default:
                throw Error("Unknown type:" + this.RegisterType);
        }
    }
}
export class FSMVG extends OBInstruction {
    Register;
    VarIdx;
    RegisterType;

    init(code, builder, instructions, i) {
        this.Register = (code & 0xFF00) >> 8;
        this.RegisterType = (code & 0xFF0000) >> 16;
        this.VarIdx = code & 0xFF;
    }

    link(builder, instructions, idx) {
        let VarIdx = this.VarIdx;
        let Register = this.Register;
        switch (this.RegisterType) {
            case 0:
                builder.LongRegister[Register] = (st, uf, l) => {
                    return st.fsm.VariableGroup.LongRegister[VarIdx];
                };
                break;
            case 1:
                builder.DoubleRegister[Register] = (st, uf, l) => {
                    return st.fsm.VariableGroup.DoubleRegister[VarIdx];
                };
                break;
            case 2:
                builder.StringRegister[Register] = (st, uf, l) => {
                    return st.fsm.VariableGroup.StringRegister[VarIdx];
                };
                break;
            case 3:
                builder.StructRegister[Register] = (st, uf, l) => {
                    return st.fsm.VariableGroup.StructRegister[VarIdx];
                };
                break;
            case 4:
                builder.NObjectRegister[Register] = (st, uf, l) => {
                    return st.fsm.VariableGroup.NObjectRegister[VarIdx];
                };
                break;
            default:
                throw Error("Unknown type " + this.RegisterType);
        }
    }
}
export class CreateFSM extends OBInstruction {
    FSMTypeName;
    ReturnRegister;

    init(code, builder, instructions, i) {
        let FSMTypeNameIdx = code & 0xFFFF;
        this.FSMTypeName = builder.loader.data.GetString(FSMTypeNameIdx);
        this.ReturnRegister = (code & 0xFF0000) >> 16;
    }

    link(builder, instructions, idx) {
        builder.NObjectRegister[this.ReturnRegister] = (st, fun, l) => {
            let fsm = st.fsm.VM.CreateFSM(this.FSMTypeName);
            if (!fsm) {
                st.fsm.VM.Log("未找到FSM类型 " + this.FSMTypeName);
            } else {
                fsm.Target = st.fsm.Target;
            }
            return fsm;
        };
    }
}
export class FSMSendMsg extends OBInstruction {
    TitleIdx;
    TargetIdx;
    BodyTypeID;
    BodyRegisterIdx;

    init(code, builder, instructions, i) {
        this.TargetIdx = ((code >> 17) & 0x7F);
        this.TitleIdx = ((code >> 10) & 0x7F);
        this.BodyTypeID = ((code >> 6) & 0xF);

        this.BodyRegisterIdx = (code & 0x3F);
    }
    /**
         *
         * @param {OBFunctionBuilder} builder
         * @param {*} instructions
         * @param {*} idx
         */
    link(builder, instructions, idx) {
        let f_title = builder.StringRegister[this.TitleIdx];
        let f_body = this.makeBody(builder);
        let f_fsm = builder.NObjectRegister[this.TargetIdx];
        builder.PushAction((st, uf, locals, pos) => {
            let fsm = f_fsm(st, uf, locals);
            if (fsm) {
                let title = f_title(st, uf, locals);
                let body = f_body(st, uf, locals);
                fsm.PostMessage(new OBUserMessage(title, OBMessage.ArgTypeOf(this.BodyTypeID, body), body, st.fsm));
            }
            return ++pos;
        });
    }
    /**
         *
         * @param {OBFunctionBuilder} builder
         * @returns
         */
    makeBody(builder) {
        let BodyRegisterIdx = this.BodyRegisterIdx;
        switch (this.BodyTypeID) {
            case 0xF:
                return (st, uf, locals) => null;
            case 0:
                var l = builder.LongRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return l(st, uf, locals);
                };
            case 1:
                var d = builder.DoubleRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return d(st, uf, locals);
                };
            case 2:
                var s = builder.StringRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s(st, uf, locals);
                };
            case 3:
                var s1 = builder.StructRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s1(st, uf, locals);
                };
            case 4:
                var n = builder.NObjectRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return n(st, uf, locals);
                };
            default:
                throw Error("Unknown type " + this.BodyTypeID);
        }
    }
}
export class GZ0 extends OBInstruction {
    VarType;
    VarIdx;
    ResultIdx;

    init(code, builder, instructions, i) {
        this.VarType = ((code >> 20) & 0xf);
        this.VarIdx = ((code >> 10) & 0x3ff);
        this.ResultIdx = (code & 0x3ff);
    }

    link(builder, instructions, idx) {
        let ResultIdx = this.ResultIdx;
        let VarIdx = this.VarIdx;
        switch (this.VarType) {
            case 0:
                let getLong = builder.LongRegister[VarIdx];
                builder.LongRegister[ResultIdx] = (st, uf, locals) => {
                    let lv = getLong(st, uf, locals);
                    return lv > 0 ? 1 : 0;
                };
                break;
            case 1:
                let getDouble = builder.DoubleRegister[VarIdx];
                builder.LongRegister[ResultIdx] = (st, uf, locals) => {
                    return getDouble(st, uf, locals) > 0 ? 1 : 0;
                };
                break;
            case 2:
                let GetString = builder.StringRegister[VarIdx];
                builder.LongRegister[ResultIdx] = (st, uf, locals) => {
                    return GetString(st, uf, locals) != null ? 1 : 0;
                };
                break;
            case 3:
                let getStruct = builder.StructRegister[VarIdx];
                builder.LongRegister[ResultIdx] = (st, uf, locals) => {
                    return getStruct(st, uf, locals) != null ? 1 : 0;
                };
                break;
            case 4:
                let getNObject = builder.NObjectRegister[VarIdx];
                builder.LongRegister[ResultIdx] = (st, uf, locals) => {
                    return getNObject(st, uf, locals) == null ? 1 : 0;
                };
                break;
            default:
                throw Error("Unknown type " + this.VarType);
        }
    }
}

export class BRIF extends OBInstruction {
    checkRegType;
    checkRegIdx;
    targetOffset;

    init(code, builder, instructions, i) {
        this.checkRegType = ((code >> 20) & 0xf);
        this.checkRegIdx = ((code >> 14) & 0x7f);
        this.targetOffset = (((code & 0x1fff) << 19) >> 19); //;// ((code) & 0x1fff);
        builder.PositionUpdate(this.targetOffset * 4, (newPos) => {
            this.targetOffset = newPos;
        });
    }

    link(builder, instructions, idx) {
        let checkRegType = this.checkRegType;
        let checkRegIdx = this.checkRegIdx;
        switch (checkRegType) {
            case 0:
                let LongReg = builder.LongRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (LongReg(st, uf, locals) != 0) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 1:
                let DoubleReg = builder.DoubleRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (DoubleReg(st, uf, locals) != 0) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 2:
                let StringReg = builder.StringRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    let str = StringReg(st, uf, locals);
                    if (str != null && !("" === (str))) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 3:
                let StructReg = builder.StructRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (StructReg(st, uf, locals) != null) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            case 4:
                let NObjectReg = builder.NObjectRegister[checkRegIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    if (NObjectReg(st, uf, locals) != null) {
                        return this.targetOffset;
                    } else {
                        return ++pos;
                    }
                });
                break;
            default:
                throw Error("Unknown type " + this.checkRegType);
        }
    }
}
export class DEC extends OBInstruction {
    regType;
    regIdx;

    init(code, builder, instructions, i) {
        this.regType = ((code >> 20) & 0xf);
        this.regIdx = ((code) & 0xfffff);
    }

    link(builder, instructions, idx) {
        let regIdx = this.regIdx;
        switch (this.regType) {
            case 0:
                let LongReg = builder.LongRegister[regIdx];
                builder.LongRegister[regIdx] = (st, uf, locals) => {
                    let v = LongReg(st, uf, locals);
                    return v - 1;
                };
                break;
            case 1:
                let DoubleReg = builder.DoubleRegister[regIdx];
                builder.DoubleRegister[regIdx] = (st, uf, locals) => {
                    let v = DoubleReg(st, uf, locals);
                    return v - 1;
                };
                break;
            case 2:
            case 3:
            case 4:
            default:
                throw Error("Unsupport type " + this.regType);
        }
    }
}
export class Reg2Var extends OBInstruction {
    type;
    varIdx;
    regIdx;

    init(code, builder, instructions, i) {
        this.type = ((code >> 20) & 0xf);
        this.regIdx = ((code >> 10) & 0x3ff);
        this.varIdx = ((code) & 0x3ff);
    }

    link(builder, instructions, idx) {
        let varIdx = this.varIdx;
        let regIdx = this.regIdx;
        switch (this.type) {
            case 0:
                let getLong = builder.LongRegister[regIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    locals.LongRegister[varIdx] = getLong(st, uf, locals);
                    return 1 + pos;
                });
                break;
            case 1:
                let getDouble = builder.DoubleRegister[regIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    locals.DoubleRegister[varIdx] = getDouble(st, uf, locals);
                    return 1 + pos;
                });
                break;
            case 2:
                let GetString = builder.StringRegister[regIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    locals.StringRegister[varIdx] = GetString(st, uf, locals);
                    return 1 + pos;
                });
                break;
            case 3:
                let getStruct = builder.StructRegister[regIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    locals.StructRegister[varIdx] = getStruct(st, uf, locals);
                    return 1 + pos;
                });
                break;
            case 4:
                let getNObject = builder.NObjectRegister[regIdx];
                builder.PushAction((st, uf, locals, pos) => {
                    locals.NObjectRegister[varIdx] = getNObject(st, uf, locals);
                    return 1 + pos;
                });
                break;
            default:
                throw Error("Unsupport type:" + this.type);
        }
    }
}
export class Var2Reg extends OBInstruction {
    type;
    varIdx;
    regIdx;

    init(code, builder, instructions, i) {
        this.type = ((code >> 20) & 0xf);
        this.regIdx = ((code >> 10) & 0x3ff);
        this.varIdx = ((code) & 0x3ff);
    }

    link(builder, instructions, idx) {
        let varIdx = this.varIdx;
        let regIdx = this.regIdx;
        switch (this.type) {
            case 0:
                builder.LongRegister[regIdx] = (st, uf, locals) => {
                    return locals.LongRegister[varIdx];
                };
                break;
            case 1:
                builder.DoubleRegister[regIdx] = (st, uf, locals) => {
                    return locals.DoubleRegister[varIdx];
                };
                break;
            case 2:
                builder.StringRegister[regIdx] = (st, uf, locals) => {
                    return locals.StringRegister[varIdx];
                };
                break;
            case 3:
                builder.StructRegister[regIdx] = (st, uf, locals) => {
                    return locals.StructRegister[varIdx];
                };
                break;
            case 4:
                builder.NObjectRegister[regIdx] = (st, uf, locals) => {
                    return locals.NObjectRegister[varIdx];
                };
                break;
            default:
                throw Error("Unsupport type:" + this.type);
        }
    }
}
export class I2F extends OBInstruction {
    intRegIdx;
    floatRegIdx;

    init(code, builder, instructions, i) {
        this.intRegIdx = ((code) >> 12) & 0xFFF;
        this.floatRegIdx = ((code) & 0xFFF);
    }

    link(builder, instructions, idx) {
        let getLong = builder.LongRegister[this.intRegIdx];
        builder.DoubleRegister[this.floatRegIdx] = (st, uf, locals) => {
            return getLong(st, uf, locals);
        };
    }
}
export class EQ extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }
    /**
         *
         * @param {OBFunctionBuilder} builder
         * @param {*} instructions
         * @param {*} idx
         */
    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                break;
            case 3:
                regArr = builder.StructRegister;
                break;
            case 4:
                regArr = builder.NObjectRegister;
                ;
                break;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) === RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}
export class NEQ extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }
    /**
         *
         * @param {OBFunctionBuilder} builder
         * @param {*} instructions
         * @param {*} idx
         */
    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                break;
            case 3:
                regArr = builder.StructRegister;
                break;
            case 4:
                regArr = builder.NObjectRegister;
                ;
                break;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) != RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}
export class LT extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }

    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                return;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) < RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}
export class LTE extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }

    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                return;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) <= RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}
export class GT extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }

    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                return;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) > RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}

export class GTE extends OBInstruction {
    ArgTypeId;
    LeftIdx;
    RightIdx;
    RetIdx;

    init(code, builder, instructions, i) {
        this.ArgTypeId = (code >> 20) & 0xF;
        this.LeftIdx = (code >> 14) & 0x3f;
        this.RightIdx = (code >> 7) & 0x7f;
        this.RetIdx = code & 0x7f;
    }

    link(builder, instructions, idx) {
        let regArr;
        switch (this.ArgTypeId) {
            case 0:
                regArr = builder.LongRegister;
                break;
            case 1:
                regArr = builder.DoubleRegister;
                break;
            case 2:
                regArr = builder.StringRegister;
                return;
            default:
                throw Error("不支持类型：" + this.ArgTypeId);
        }
        let LeftReg = regArr[this.LeftIdx];
        let RightReg = regArr[this.RightIdx];
        builder.LongRegister[this.RetIdx] = (st, uf, locals) => {
            return LeftReg(st, uf, locals) >= RightReg(st, uf, locals) ? 1 : 0;
        };
    }
}
export class DestroyFSM extends OBInstruction {
    link(builder, instructions, idx) {
        builder.PushAction((st, uf, locals, pos) => {
            st.fsm.Destroy();
            throw new ChangeDestroyException();
        });
    }
}
export class FSMBroadcastMsg extends OBInstruction {
    TitleIdx;
    BodyTypeID;
    BodyRegisterIdx;

    init(code, builder, instructions, i) {
        this.TitleIdx = ((code >> 10) & 0x7F);
        this.BodyTypeID = ((code >> 6) & 0xF);
        this.BodyRegisterIdx = (code & 0x3F);
    }

    link(builder, instructions, idx) {
        let f_title = builder.StringRegister[this.TitleIdx];
        let f_body = this.makeBody(builder);
        builder.PushAction((st, uf, locals, pos) => {
            let title = f_title(st, uf, locals);
            let body = f_body(st, uf, locals);
            st.fsm.VM.BroadcastMessage(new OBUserMessage(title, OBMessage.ArgTypeOf(this.BodyTypeID, body), body, st.fsm));
            return ++pos;
        });
    }
    /**
         *
         * @param {OBFunctionBuilder} builder
         * @returns
         */
    makeBody(builder) {
        switch (this.BodyTypeID) {
            case 0xF:
                return (st, uf, locals) => null;
            case 0:
                var l = builder.LongRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return l(st, uf, locals);
                };
            case 1:
                var d = builder.DoubleRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return d(st, uf, locals);
                };
            case 2:
                var s = builder.StringRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s(st, uf, locals);
                };
            case 3:
                var s1 = builder.StructRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s1(st, uf, locals);
                };
            case 4:
                var n = builder.NObjectRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return n(st, uf, locals);
                };
            default:
                throw Error("Unknown type " + this.BodyTypeID);
        }
    }
}

/**
 * 单元数操作
 */
export class SGLF extends OBInstruction {
    Opcode;
    value;

    init(code, builder, instructions, i) {
        this.value = (code & 0xFFFF);
        this.Opcode = (code >> 16) & 0xff;
    }

    link(builder, instructions, idx) {
        let value = this.value;
        let f_value = builder.DoubleRegister[this.value];
        if (f_value == null) {
            throw Error("left is null");
        }
        switch (this.Opcode) {
            case 0:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return -f_value(s, f, l);
                };
                break;
            case 1:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.log(f_value(s, f, l));
                };
                break;
            case 2:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.log10(f_value(s, f, l));
                };
                break;
            case 3:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.exp(f_value(s, f, l));
                };
                break;
            case 4:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.pow(10, f_value(s, f, l));
                };
                break;
            case 5:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.sqrt(f_value(s, f, l));
                };
                break;
            case 6:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.abs(f_value(s, f, l));
                };
                break;
            case 7:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.sin(f_value(s, f, l));
                };
                break;
            case 8:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.cos(f_value(s, f, l));
                };
                break;
            case 9:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.tan(f_value(s, f, l));
                };
                break;
            case 10:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.asin(f_value(s, f, l));
                };
                break;
            case 11:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.acos(f_value(s, f, l));
                };
                break;
            case 12:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.atan(f_value(s, f, l));
                };
                break;
            case 13:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.round(f_value(s, f, l));
                };
                break;
            case 14:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.ceil(f_value(s, f, l));
                };
                break;
            case 15:
                builder.DoubleRegister[value] = (s, f, l) => {
                    return Math.floor(f_value(s, f, l));
                };
                break;
        }
    }
}
export class RAND extends OBInstruction {
    Register;

    init(code, builder, instructions, i) {
        this.Register = (code & 0xFFFFFF);
    }

    link(builder, instructions, idx) {
        builder.DoubleRegister[this.Register] = (st, f, l) => {
            return Math.random();
        };
    }
}
export class F2I extends OBInstruction {
    intRegIdx;
    floatRegIdx;

    init(code, builder, instructions, i) {
        this.intRegIdx = ((code) >> 12) & 0xFFF;
        this.floatRegIdx = ((code) & 0xFFF);
    }

    link(builder, instructions, idx) {
        let g = builder.DoubleRegister[this.floatRegIdx];
        builder.LongRegister[this.intRegIdx] = (st, uf, locals) => {
            return Math.trunc(g(st, uf, locals));
        };
    }
}
export class FSMSendMsgWait_Data extends OBInstruction {
    TitleIdx;
    TargetIdx;
    BodyTypeID;
    BodyRegisterIdx;

    init(code, builder, instructions, i) {
        this.TargetIdx = ((code >> 17) & 0x7F);
        this.TitleIdx = ((code >> 10) & 0x7F);
        this.BodyTypeID = ((code >> 6) & 0xF);
        this.BodyRegisterIdx = (code & 0x3F);
    }
}
export class FSMSendMsgWait extends OBInstruction {
    waitMilliSecondIdx;

    init(code, builder, instructions, i) {
        this.waitMilliSecondIdx = ((code >> 17) & 0x3F);
    }

    link(builder, instructions, idx) {
        let anchor = instructions[idx - 1];
        if (!(anchor instanceof FSMSendMsgWait_Data)) {
            throw Error("字节码错误");
        }
        let f_title = builder.StringRegister[anchor.TitleIdx];
        let f_body = this.makeBody(builder, anchor.BodyTypeID, anchor.BodyRegisterIdx);
        let waitTime = builder.LongRegister[this.waitMilliSecondIdx];
        let f_fsm = builder.NObjectRegister[anchor.TargetIdx];
        builder.PushAction((st, uf, locals, pos) => {
            let title = f_title(st, uf, locals);
            let body = f_body(st, uf, locals);
            let waitSecond = waitTime(st, uf, locals);
            let FSM = st.fsm;
            let fsm = f_fsm(st, uf, locals);
            if (!fsm) {
                FSM.VM.Log("未找到发送目标");
                return;
            }
            if (!(fsm instanceof OBVMFSM)) {
                throw Error("字节码错误");
            }
            fsm.VM.Schedule(waitSecond, (VM) => {
                fsm.PostMessage(new OBUserMessage(title, OBMessage.ArgTypeOf(anchor.BodyTypeID, body), body, FSM));
            });
            return ++pos;
        });
    }

    makeBody(builder, BodyTypeID, BodyRegisterIdx) {
        switch (BodyTypeID) {
            case 0xF:
                return (st, uf, locals) => null;
            case 0:
                var l = builder.LongRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return l(st, uf, locals);
                };
            case 1:
                var d = builder.DoubleRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return d(st, uf, locals);
                };
            case 2:
                var s = builder.StringRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s(st, uf, locals);
                };
            case 3:
                var s1 = builder.StructRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s1(st, uf, locals);
                };
            case 4:
                var n = builder.NObjectRegister[BodyRegisterIdx];
                return (st, uf, locals) => {
                    return n(st, uf, locals);
                };
            default:
                throw Error("Unknown type " + BodyTypeID);
        }
    }
}
export class FSMBroadcastMsgWait extends OBInstruction {
    TitleIdx;
    BodyTypeID;
    BodyRegisterIdx;
    waitMilliSecondIdx;

    init(code, builder, instructions, i) {
        this.TitleIdx = ((code >> 10) & 0x7F);
        this.BodyTypeID = ((code >> 6) & 0xF);

        this.BodyRegisterIdx = (code & 0x3F);
        this.waitMilliSecondIdx = ((code >> 17) & 0x3F);
        ;
    }

    link(builder, instructions, idx) {
        let f_title = builder.StringRegister[this.TitleIdx];
        let f_body = this.makeBody(builder);
        let waitTime = builder.LongRegister[this.waitMilliSecondIdx];
        builder.PushAction((st, uf, locals, pos) => {
            let title = f_title(st, uf, locals);
            let body = f_body(st, uf, locals);
            let waitSecond = waitTime(st, uf, locals);
            let fsm = st.fsm;
            fsm.VM.Schedule(waitSecond, (VM) => {
                VM.BroadcastMessage(new OBUserMessage(title, OBMessage.ArgTypeOf(this.BodyTypeID, body), body, fsm));
            });
            return ++pos;
        });
    }

    makeBody(builder) {
        switch (this.BodyTypeID) {
            case 0xF:
                return (st, uf, locals) => null;
            case 0:
                var l = builder.LongRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return l(st, uf, locals);
                };
            case 1:
                var d = builder.DoubleRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return d(st, uf, locals);
                };
            case 2:
                var s = builder.StringRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s(st, uf, locals);
                };
            case 3:
                var s1 = builder.StructRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return s1(st, uf, locals);
                };
            case 4:
                var n = builder.NObjectRegister[this.BodyRegisterIdx];
                return (st, uf, locals) => {
                    return n(st, uf, locals);
                };
            default:
                throw Error("Unknown type " + this.BodyTypeID);
        }
    }
}
export class TextJoin extends OBInstruction {
    Left;
    Right;
    RetReg;

    init(code, builder, instructions, i) {
        this.Left = (code >> 17) & 0xFF;
        this.Right = (code >> 9) & 0xff;
        this.RetReg = (code & 0xFF);
    }

    link(builder, instructions, idx) {
        var l = builder.StringRegister[this.Left];
        var r = builder.StringRegister[this.Right];
        builder.StringRegister[this.RetReg] = (st, f, local) => {
            return l(st, f, local) + r(st, f, local);
        };
    }
}
export class ToString extends OBInstruction {
    ValueType;
    ValueRegIdx;
    RetRegIdx;

    init(code, builder, instructions, i) {
        this.ValueType = (code >> 20) & 0xF;
        this.ValueRegIdx = (code >> 10) & 0x1ff;
        this.RetRegIdx = (code & 0x1FF);
    }

    link(builder, instructions, idx) {
        switch (this.ValueType) {
            case 0:
                var getl = builder.LongRegister[this.ValueRegIdx];
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    return getl(st, f, l).toString();
                };
                break;
            case 1:
                var getd = builder.DoubleRegister[this.ValueRegIdx];
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    return getd(st, f, l).toString();
                };
                break;
            case 2:
                var getstr = builder.StringRegister[this.ValueRegIdx];
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    var v = getstr(st, f, l);
                    if (v == null) {
                        return "";
                    }
                    return v;
                };
                break;
            case 3:
                var getstruct = builder.StructRegister[this.ValueRegIdx];
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    var v = getstruct(st, f, l);
                    if (v == null) {
                        return "";
                    }
                    return v.toString();
                };
                break;
            case 4:
                var geto = builder.NObjectRegister[this.ValueRegIdx];
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    var v = geto(st, f, l);
                    if (v == null) {
                        return "";
                    }
                    return v.toString();
                };
                break;
            default:
                throw Error("Unknown type:" + this.ValueType);
        }
    }
}
export class Sender extends OBInstruction {
    RetReg;

    init(code, builder, instructions, i) {
        this.RetReg = code & 0xFFFF;
    }

    link(builder, instructions, idx) {
        builder.NObjectRegister[this.RetReg] = (st, f, local) => {
            return st.CurrentMessageSender();
        };
    }
}
export class SHL extends OBInstruction {
    value; bitCount;
    init(code, builder, instructions, i) {
        this.bitCount = code & 0xFFF;
        this.value = (code & 0xFFF000) >> 12;
    }
    link(builder, instructions, idx) {
        let getV = builder.LongRegister[this.value];
        let getC = builder.LongRegister[this.bitCount];
        builder.LongRegister[this.value] = (st, f, l) => {
            let v = getV(st, f, l);
            let c = getC(st, f, l);
            if (c > 0) {
                let r = v << c;
                return r;
            } else {
                let r = v >> -c;
                return r;
            }

        };
    }
}
export class AND extends OBInstruction {
    a; b;
    init(code, builder, instructions, i) {
        this.b = code & 0xFFF;
        this.a = (code & 0xFFF000) >> 12;
    }
    link(builder, instructions, idx) {
        let getV = builder.LongRegister[this.a];
        let getC = builder.LongRegister[this.b];
        builder.LongRegister[this.a] = (st, f, l) => {
            let v = getV(st, f, l);
            let c = getC(st, f, l);
            let r = v & c;
            return r;
        };
    }
}
export class FIX extends OBInstruction {
    regType; regIdx;
    init(code, builder, instructions, i) {
        this.regIdx = code & 0xFFFFF;
        this.regType = (code & 0xF00000) >> 20;
    }
    link(builder, instructions, idx) {
        let v;
        let loaded = false;
        let type;
        switch (this.regType) {
            case 0:
                type = 'LongRegister';
                break;
            case 1:
                type = 'DoubleRegister';
                break;
            case 2:
                type = 'StringRegister';
                break;
            case 3:
                type = 'StructRegister';
                break;
            case 4:
                type = 'NObjectRegister';
                break;
            default:
                throw Error("Unknown type:" + this.regType);
        }
        let getV = builder[type][this.regIdx];
        builder[type][this.regIdx] = (st, f, l) => {
            if (!loaded) {
                v = getV(st, f, l);
            } else {
                loaded = true;
            }
            return v;
        };
    }
}
/**
 * value of may by key
 */
export class VOM extends OBInstruction {
    map;
    key;
    ValueType;
    RetRegIdx;
    init(code, builder, instructions, i) {
        this.ValueType = code & 0x3F;
        this.RetRegIdx = (code & (0x3f << 6)) >> 6;
        this.key = (code & (0x3f << 12)) >> 12;
        this.map = (code & (0x3f << 18)) >> 18;
    }

    link(builder, instructions, idx) {
        var getMap = builder.StructRegister[this.map];
        var getKey = builder.StringRegister[this.key];
        switch (this.ValueType) {
            case 0:
                builder.LongRegister[this.RetRegIdx] = (st, f, l) => {
                    let m = getMap(st, f, l);
                    let k = getKey(st, f, l);
                    let v = m[k];
                    return v;
                };
                break;
            case 1:
                builder.DoubleRegister[this.RetRegIdx] = (st, f, l) => {
                    let m = getMap(st, f, l);
                    let k = getKey(st, f, l);
                    let v = m[k];
                    return v;
                };
                break;
            case 2:
                builder.StringRegister[this.RetRegIdx] = (st, f, l) => {
                    let m = getMap(st, f, l);
                    let k = getKey(st, f, l);
                    let v = m[k];
                    return v;
                };
                break;
            case 3:
                builder.StructRegister[this.RetRegIdx] = (st, f, l) => {
                    let m = getMap(st, f, l);
                    let k = getKey(st, f, l);
                    let v = m[k];
                    if (!v) {
                        debugger
                    }
                    return v;
                };
                break;
            case 4:
                builder.NObjectRegister[this.RetRegIdx] = (st, f, l) => {
                    let m = getMap(st, f, l);
                    let k = getKey(st, f, l);
                    let v = m[k];
                    return v;
                };
                break;
            default:
                throw Error("Unknown type:" + this.ValueType);
        }
    }
}
export class LAND extends OBInstruction {
    a; b;
    init(code, builder, instructions, i) {
        this.b = code & 0xFFF;
        this.a = (code & 0xFFF000) >> 12;
    }
    link(builder, instructions, idx) {
        let getV = builder.LongRegister[this.a];
        let getC = builder.LongRegister[this.b];
        builder.LongRegister[this.a] = (st, f, l) => {
            let v = getV(st, f, l);
            if (v) {
                let c = getC(st, f, l);
                return c ? 1 : 0;
            } else {
                return 0;
            }
        };
    }
}
export class LOR extends OBInstruction {
    a; b;
    init(code, builder, instructions, i) {
        this.b = code & 0xFFF;
        this.a = (code & 0xFFF000) >> 12;
    }
    link(builder, instructions, idx) {
        let getV = builder.LongRegister[this.a];
        let getC = builder.LongRegister[this.b];
        builder.LongRegister[this.a] = (st, f, l) => {
            let v = getV(st, f, l);
            if (v) {
                return 1;
            } else {
                let c = getC(st, f, l);
                return c ? 0 : 1;
            }
        };
    }
}
export class LNOT extends OBInstruction {
    a;
    init(code, builder, instructions, i) {
        this.a = code & 0xFFF;
    }
    link(builder, instructions, idx) {
        let getV = builder.LongRegister[this.a];
        builder.LongRegister[this.a] = (st, f, l) => {
            let v = getV(st, f, l);
            return v === 0 ? 1 : 0;
        };
    }
}
export class COND extends OBInstruction {
    if_; then_; else_; regType;
    init(code, builder, instructions, i) {
        this.regType = code & 0x3F;
        this.else_ = (code & (0x3f << 6)) >> 6;
        this.then_ = (code & (0x3f << 12)) >> 12;
        this.if_ = (code & (0x3f << 18)) >> 18;
    }
    link(builder, instructions, idx) {
        let if_ = builder.LongRegister[this.if_];
        let else_;
        let then_;
        let type;
        switch (this.regType) {
            case 0:
                type = 'LongRegister';
                break;
            case 1:
                type = 'DoubleRegister';
                break;
            case 2:
                type = 'StringRegister';
                break;
            case 3:
                type = 'StructRegister';
                break;
            case 4:
                type = 'NObjectRegister';
                break;
            default:
                throw Error("Unknown type:" + this.regType);
        }
        else_ = builder[type][this.else_];
        then_ = builder[type][this.then_];
        builder[type][this.then_] = (st, f, l) => {
            let if_v = if_(st, f, l);
            if (if_v != 0) {
                return then_(st, f, l);
            }
            return else_(st, f, l);
        };
    }
}
export class NEW extends OBInstruction {
    StructDef;
    Register;

    init(code, builder, instructions, i) {
        let stridx = code & 0xFFF;
        this.Register = (code & 0xFF0000) >> 16;

        let structType = builder.loader.data.GetString(stridx);
        let structDef = builder.loader.script.StructDef[structType];
        if (!structDef) {
            throw Error('不存在数据结构 ' + structType);
        }
        this.StructDef = structDef;
    }

    link(builder, instructions, idx) {
        builder.StructRegister[this.Register] = this.getValue.bind(this);
    }

    getValue(UBState, obvmfunction, TypedRegisters) {
        let s = new OBStructValue(this.StructDef);
        return s;
    }
}

export class OBStructDataSerializer {
    relocation = new Relocation();
    /**
     * 
     * @param {OBStructValue[]} valueData 
     * @returns {int[]}
     */
    serialize(valueDataArray) {
        let group = this.groupData(valueDataArray);
    }
    /**
     * 
     * @param {OBStructValue[]} valueDataArray 
     * @returns {Object.<String,OBStructValue[]}
     */
    groupData(valueDataArray) {
        let group = {};
        valueDataArray.forEach(d => {
            let list = group[d.Def.Name];
            if (!list) {
                list = [];
                group[d.Def.Name] = list;
            }
            this.collData(d);
            list.push(d);
        });
        return group;
    }
    /**
     * 
     * @param {OBStructValue} d 
     */
    collData(d) {
        d.string.forEach(str => {
            this.relocation.addRelocationString(str);
        });
        d.registers.object.forEach(ofd => {
            switch (ofd.type.$__type) {
                case "StructFieldTypeStruct":
                    break;
                case "StructFieldTypeIntegerMap":
                    switch (ofd.type.elementType.name) {
                        case 'string':
                        case 'String':
                            Object.values(item.registers.object[ofd.registerIndex]).forEach(str => {
                                this.addRelocationString(str);
                            });
                    }
                    break;
                case "StructFieldTypeStringMap":
                    switch (ofd.type.elementType.name) {
                        case 'string':
                        case 'String':
                            Object.values(item.registers.object[ofd.registerIndex]).forEach(str => {
                                this.addRelocationString(str);
                            });
                    }
                    Object.keys(item.registers.object[ofd.registerIndex]).forEach(str => {
                        this.addRelocationString(str);
                    });
                    break;
                case "StructFieldTypeList":
                    switch (ofd.type.elementType.name) {
                        case 'string':
                        case 'String':
                            Object.values(item.registers.object[ofd.registerIndex]).forEach(str => {
                                this.addRelocationString(str);
                            });
                    }
                    break;
                default:
                    console.error(ofd);
                    throw Error("功能未实现 " + ofd.type.$__type);
            }
        });
    }
}